Session 15: CSP bugs fixed, QA passed, marketing materials ready
This commit is contained in:
parent
0cefaf71d1
commit
58bbc9965d
9 changed files with 456 additions and 19 deletions
146
projects/business/marketing/devto-article.md
Normal file
146
projects/business/marketing/devto-article.md
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
---
|
||||
title: "I Built an HTML-to-PDF API in a Day — Here's How It Works"
|
||||
published: false
|
||||
tags: node, javascript, api, pdf
|
||||
---
|
||||
|
||||
Every developer has that one problem they keep solving over and over. For me, it's PDF generation.
|
||||
|
||||
Invoice for a freelance project? PDF. Report for a dashboard? PDF. Receipt for an e-commerce app? PDF. And every time, I'd end up in the same rabbit hole: installing wkhtmltopdf, fighting with Docker images, or configuring Puppeteer from scratch.
|
||||
|
||||
So I built [DocFast](https://docfast.dev) — a simple API that takes HTML or Markdown and returns a PDF. Here's how it works under the hood.
|
||||
|
||||
## The Architecture
|
||||
|
||||
DocFast is straightforward:
|
||||
|
||||
- **Express** handles the API layer
|
||||
- **Puppeteer** (headless Chromium) does the actual rendering
|
||||
- A **pool of browser instances** keeps things fast
|
||||
|
||||
The core flow is:
|
||||
|
||||
1. You POST HTML/Markdown + options to the API
|
||||
2. The server loads it into a pooled Chromium page
|
||||
3. Chromium renders it and exports to PDF
|
||||
4. You get the PDF back
|
||||
|
||||
That's it. No queue, no webhook callback, no async polling. Request in, PDF out.
|
||||
|
||||
## Why Puppeteer Over wkhtmltopdf?
|
||||
|
||||
I tried both. wkhtmltopdf is fast but uses an ancient WebKit engine — modern CSS (flexbox, grid, custom properties) breaks in weird ways. Puppeteer uses actual Chromium, so if it looks right in Chrome, it looks right in the PDF.
|
||||
|
||||
The tradeoff is resource usage. Chromium is heavy. That's where the browser pool comes in — instead of launching a new browser per request, I maintain a pool of warm instances. A new page in an existing browser takes ~50ms vs ~2s for a cold start.
|
||||
|
||||
## API Design Choices
|
||||
|
||||
I wanted the API to feel obvious. If you've used any REST API, you already know how to use DocFast.
|
||||
|
||||
### Basic HTML to PDF
|
||||
|
||||
```bash
|
||||
curl -X POST https://docfast.dev/v1/convert/html \
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"html": "<h1>Invoice #1042</h1><p>Amount: $250.00</p>"}' \
|
||||
--output invoice.pdf
|
||||
```
|
||||
|
||||
### Markdown to PDF
|
||||
|
||||
```bash
|
||||
curl -X POST https://docfast.dev/v1/convert/html \
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"markdown": "# Monthly Report\n\n| Metric | Value |\n|--------|-------|\n| Users | 1,204 |"}' \
|
||||
--output report.pdf
|
||||
```
|
||||
|
||||
### With Options
|
||||
|
||||
```javascript
|
||||
const response = await fetch('https://docfast.dev/v1/convert/html', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Bearer YOUR_API_KEY',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
html: '<h1>Styled Doc</h1>',
|
||||
css: 'h1 { color: navy; font-family: Georgia; }',
|
||||
options: {
|
||||
format: 'A4',
|
||||
margin: { top: '20mm', bottom: '20mm' },
|
||||
header: '<div style="font-size:10px;text-align:right;">Page <span class="pageNumber"></span></div>',
|
||||
footer: '<div style="font-size:10px;text-align:center;">Confidential</div>'
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const pdf = await response.blob();
|
||||
```
|
||||
|
||||
A few design decisions I'm happy with:
|
||||
|
||||
- **HTML and Markdown in the same endpoint.** Send `html` or `markdown` — the API figures it out. No separate routes.
|
||||
- **CSS as a separate field.** Keeps your HTML clean. You can also inline it, obviously.
|
||||
- **Sensible defaults.** If you send just `{"html": "..."}`, you get an A4 PDF with reasonable margins. No required options.
|
||||
|
||||
## Built-in Templates
|
||||
|
||||
This is the part I'm most excited about. DocFast ships with templates for common document types:
|
||||
|
||||
```javascript
|
||||
const response = await fetch('https://docfast.dev/v1/convert/html', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Bearer YOUR_API_KEY',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
template: 'invoice',
|
||||
data: {
|
||||
company: 'Acme Inc.',
|
||||
items: [
|
||||
{ description: 'Web Development', quantity: 40, rate: 150 },
|
||||
{ description: 'Design Review', quantity: 5, rate: 150 }
|
||||
],
|
||||
due_date: '2026-03-01'
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
You pass structured data, the template handles layout and styling. No HTML authoring needed for standard documents. Currently there are templates for invoices, receipts, and reports — more coming based on what people actually need.
|
||||
|
||||
## Performance
|
||||
|
||||
Some rough numbers from production:
|
||||
|
||||
- Simple HTML (text, basic styling): **~200ms**
|
||||
- Complex HTML (tables, images, custom fonts): **~800ms**
|
||||
- Template with data injection: **~400ms**
|
||||
|
||||
Most of the time is Chromium rendering. The API overhead is minimal. For comparison, spinning up Puppeteer yourself (cold start in a Lambda/Cloud Function) typically takes 3-8 seconds.
|
||||
|
||||
## Pricing and Why There's a Free Tier
|
||||
|
||||
Free tier: **100 PDFs/month**, no credit card required. Enough for side projects, prototyping, or low-volume internal tools.
|
||||
|
||||
Pro: **$9/month for 10,000 PDFs**. That's $0.0009 per PDF.
|
||||
|
||||
I wanted the free tier to be actually usable, not a glorified trial. If you're generating fewer than 100 PDFs a month, you never need to pay. If your project takes off and you need more, $9/mo is less than most developers spend on coffee in a week.
|
||||
|
||||
## What I'd Do Differently
|
||||
|
||||
If I rebuilt this from scratch:
|
||||
|
||||
- **I'd start with templates earlier.** The raw HTML→PDF conversion was the obvious MVP, but templates are what people actually get excited about.
|
||||
- **I'd add a preview endpoint sooner.** Being able to see the PDF in-browser before committing to generation saves a lot of back-and-forth.
|
||||
|
||||
## Try It
|
||||
|
||||
The docs are at [docfast.dev/docs](https://docfast.dev/docs). You can grab an API key in about 30 seconds.
|
||||
|
||||
If you have feedback on the API design, templates you'd want, or bugs — I'm all ears. Building this solo, so every piece of feedback actually gets read.
|
||||
Loading…
Add table
Add a link
Reference in a new issue