Project seven, PDF toolkit
Separate HTML-to-PDF, asynchronous upload processing, and heavy native work with R2, Queue, and a job-status API.
Edited and verified by Orange Book Editorial Team ·
Completion criteria
HTML or a URL can produce a PDF; an uploaded object creates an asynchronous job; users can query queued, running, done, and failed; duplicate messages do not create conflicting results; oversized, encrypted, timed-out, and processor-failure cases are explicit.
Prerequisites and companion example
The companion implements one real, bounded html-to-pdf operation first. Unit tests replace the Browser Run binding completely, so they need no account and consume no remote quota:
node --test examples/pdf-tool/test/index.test.mjsNine tests should pass, covering tenant-scoped status, a minimal Queue message, unsafe-HTML rejection, D1 failure compensation, PDF signatures, deterministic output keys, duplicate delivery, retry, and final failure. The full Worker also needs D1, R2, a primary and dead-letter Queue, and Browser Run. browser.remote is true as required for current Quick Actions local development, so starting the complete flow requires Cloudflare login and consumes Browser Run usage.
cp examples/pdf-tool/.dev.vars.example examples/pdf-tool/.dev.vars
pnpm wrangler d1 migrations apply orange-book-pdf-jobs --local --config examples/pdf-tool/wrangler.jsonc
pnpm wrangler dev --config examples/pdf-tool/wrangler.jsoncProcessing capability boundary
This example turns safe HTML into a PDF. It does not claim to merge, split, OCR, or parse arbitrary uploaded PDFs. Route those tasks to a separately limited and audited Container or external processor.
Classify the PDF task first
| Task | Cloudflare starting point | Request model |
|---|---|---|
| Generate invoice, report, or certificate from HTML/URL | Browser Run /pdf | A short request can trigger it; store the result in R2 |
| Summarize, index, or extract after upload | R2 event notification + Queue + Worker | Asynchronous |
| Merge, split, OCR, complex native library, large file | Evaluate a Container or external processor | Asynchronous with resource limits |
Browser Run /pdf accepts url or html. It renders controlled pages into documents; it is not a general-purpose PDF editor. Cloudflare's official post-upload summarization tutorial uses R2 event notifications, Queues, and Workers AI, which demonstrates why file processing should be decoupled from the upload request.
Job state model
D1 stores job ID, tenant, input key, operation, status, attempt, output key, and error category. R2 stores input and output objects. Queue messages remain small and contain stable IDs and controlled object keys, not full files or secrets.
await env.PDF_JOBS.send({
jobId,
tenantId,
inputKey,
operation: 'extract-text',
});
return Response.json(
{ jobId, status: 'queued' },
{ status: 202 },
);The consumer reloads parameters from the trusted job record and writes a unique output key. On duplicate delivery, it checks job/version. A completed job is acknowledged without overwriting a different-version result.
Complete the processing flow
Create an upload contract
Authenticate the user, bound the declared size, allow application/pdf, and generate a tenant-scoped object key and job ID. The browser never chooses the bucket or an arbitrary target path.
Verify after upload
Read object metadata from R2 and check actual size, media type, ownership, and job relationship. Encrypted, corrupted, or unsupported PDFs enter an explicit failure state.
Dispatch to the correct processor
HTML generation uses Browser Run. Text extraction or summarization is evaluated against current Worker and model limits. Native binaries, long CPU, or large memory move to a Container or external processor. Do not use chunking to hide a runtime mismatch.
Store a recoverable result
Write output to a versioned R2 key, then conditionally mark the job done. If the database update fails, retry discovers the same output. A delayed cleanup job handles orphan objects.
Expose status and download
The status endpoint returns jobs for the current user/tenant only. Download reauthorizes, applies media type, filename, and cache policy, and never reveals the internal bucket key.
Security and cost
Input
Isolation
Abuse
Privacy
If you add summarization or classification, it is an optional AI step inside the processor. PDF generation, storage, authorization, and download continue to work when AI is disabled.
Verification matrix
Test a valid small file, empty file, wrong MIME, oversized file, encrypted/corrupt file, duplicate message, processor timeout, R2 write failure, D1 update failure, cross-tenant job ID, and deleted output. Prove at least once that duplicate consumption does not create two business results.
Verification and troubleshooting
| Symptom | Check first | Recovery |
|---|---|---|
Job creation returns invalid_or_unsafe_html | Script, iframe, external URL, or size violation | Use a self-contained controlled template without active content |
Response is queue_unavailable | Producer binding and Queue exist | Keep the failed job record and explicitly create a new job after recovery |
Job stays running | Consumer, retries, and Browser Run usage | Inspect Queue/Browser metrics; do not resubmit blindly |
Final state is processor_failed | Browser response, input object, and quota | Fix the cause and create a new version; never overwrite the old output key |
Remote boundary and rollback
Browser Run bindings, R2 event notifications, Queue consumers, Containers, and remote buckets change external state. This tutorial does not create them automatically. Code rollback cannot retract old messages already in the queue. Include a message version and move unsupported versions to a failed/dead-letter state. Deletion first revokes download access and marks expiry, then removes input and output after a delay.
Next: run the production release checklist.
Primary sources
Did this page help you complete your goal?
Beta feedback is generated in this browser and is never uploaded automatically.
Project six, image converter
Use R2 for originals, Cloudflare Images for supported transformations, and explicit parameter, source, cost, and cache boundaries.
Project one safely ship an AI-generated static site
Review an AI-generated frontend, validate home and 404 routes with Workers Static Assets, and prepare a reversible release.