Serverless PDF Reports with Lambda and Vercel

Serverless functions (AWS Lambda, Vercel Functions, etc.) are great for on-demand work—but they're not a good place to run headless Chrome. Memory limits, cold starts, and binary size make PDF generation inside the function painful. The solution: keep your logic in the function and call a PDF API to do the actual rendering. Here's how to generate PDF reports from Lambda or Vercel using Doppio.

Why not run Chrome in the function?

Headless Chrome needs hundreds of MB of RAM and a large deployment package (or a Lambda layer). In serverless, you pay for duration and memory; long, heavy runs get expensive and can hit timeouts. An API call is a few seconds and a few KB of payload—no browser, no scaling headaches.

Flow overview

  1. Your function runs (e.g. on HTTP request or schedule).
  2. It builds the report data and optionally renders HTML (or uses a Doppio template ID + templateData).
  3. It POSTs to Doppio's /v1/render/pdf/sync for HTML, or /v1/template/sync for saved templates.
  4. Doppio returns a document URL (or binary with /direct). Your function returns that URL to the client, uploads the PDF to S3, or sends it by email.

Example: Vercel serverless function

In a Vercel project, create an API route that calls Doppio and returns the PDF URL (or streams the PDF). Store your API key in environment variables.

// api/generate-report.js
export default async function handler(req, res) {
  const html = `<!DOCTYPE html><html><body>
    <h1>Report</h1>
    <p>Generated at ${new Date().toISOString()}</p>
  </body></html>`;

  const htmlBase64 = Buffer.from(html, 'utf8').toString('base64');

  const response = await fetch('https://api.doppio.sh/v1/render/pdf/sync', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.DOPPIO_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      page: {
        setContent: { html: htmlBase64 },
        pdf: { format: 'A4', printBackground: true },
      },
    }),
  });

  const data = await response.json();
  res.status(200).json({ pdfUrl: data.documentUrl });
}

Example: AWS Lambda (Node.js)

Same idea in Lambda: use fetch or axios to call Doppio, pass base64-encoded HTML or call the template API with templateData, and return or store the result. For large reports or batch jobs, use Doppio's async endpoint and a webhook so the function returns immediately and processes the PDF when it's ready.

const response = await fetch('https://api.doppio.sh/v1/template/sync', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.DOPPIO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    templateId: process.env.DOPPIO_REPORT_TEMPLATE_ID,
    templateData: {
      DOP_period: '2026-Q1',
      DOP_userId: event.queryStringParameters?.userId,
    },
  }),
});
const { documentUrl } = await response.json();
return { statusCode: 200, body: JSON.stringify({ pdfUrl: documentUrl }) };

Uploading to S3 or sending by email

After you get documentUrl, your function can fetch(documentUrl) to get the PDF buffer, then upload it to S3 (or attach it to an email via SendGrid, SES, etc.). Doppio also supports presigned URLs: you can ask Doppio to upload the PDF directly to your bucket, so the function doesn't have to stream the file.

Summary

Serverless and PDF generation work well together when the function only orchestrates: build data, call Doppio, return or store the result. No Chrome in the runtime, no extra layers, and scaling stays on Doppio's side. For more, see the Doppio documentation and the Node.js package if you prefer a typed client.

Serverless PDF reports with Lambda and Vercel