Puppeteer works fine locally, but breaks when you deploy to a serverless environment. Chromium's unzipped size exceeds AWS Lambda's 250 MB code limit. Vercel Edge Functions block Node.js native modules. Cloudflare Workers have no file system.
A hosted REST API sidesteps every one of these constraints. From serverless, PDF generation is just an HTTP POST.
Vercel: App Router Route Handler
export async function POST(request: Request) {
const { html } = await request.json()
const res = await fetch('https://platform.htmltopdfapi.co/api/v1/pdf/generate', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HTMLTOPDF_API_KEY!}`,
'Content-Type': 'application/json',
Accept: 'application/pdf',
},
body: JSON.stringify({ html, paper_size: 'a4' }),
})
if (!res.ok) return Response.json({ error: 'PDF failed' }, { status: 500 })
return new Response(await res.arrayBuffer(), {
headers: { 'Content-Type': 'application/pdf' },
})
}AWS Lambda (Node.js)
import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'
export const handler: APIGatewayProxyHandlerV2 = async (event) => {
const { html } = JSON.parse(event.body ?? '{}') as { html: string }
const res = await fetch('https://platform.htmltopdfapi.co/api/v1/pdf/generate', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HTMLTOPDF_API_KEY}`,
'Content-Type': 'application/json',
Accept: 'application/pdf',
},
body: JSON.stringify({ html, paper_size: 'a4' }),
})
if (!res.ok) return { statusCode: 500, body: JSON.stringify({ error: 'PDF failed' }) }
return {
statusCode: 200,
headers: { 'Content-Type': 'application/pdf' },
body: Buffer.from(await res.arrayBuffer()).toString('base64'),
isBase64Encoded: true,
}
}Cloudflare Workers
export default {
async fetch(request: Request, env: { HTMLTOPDF_API_KEY: string }): Promise<Response> {
const { html } = await request.json() as { html: string }
const res = await fetch('https://platform.htmltopdfapi.co/api/v1/pdf/generate', {
method: 'POST',
headers: {
Authorization: `Bearer ${env.HTMLTOPDF_API_KEY}`,
'Content-Type': 'application/json',
Accept: 'application/pdf',
},
body: JSON.stringify({ html, paper_size: 'a4' }),
})
if (!res.ok) return Response.json({ error: 'PDF failed' }, { status: 500 })
return new Response(res.body, { headers: { 'Content-Type': 'application/pdf' } })
},
}Why This Works Everywhere
The pattern is identical across every serverless platform: send an HTTP POST, receive PDF bytes. No binary to bundle, no native module to compile, no Lambda layer to maintain. The rendering happens on the API infrastructure. Your function is just a thin proxy.