Node.js5 min read · June 6, 2026

Generate PDFs from HTML in Node.js Using a REST API

Skip Puppeteer's infrastructure overhead. Generate HTML to PDF in Node.js with a simple fetch call — works in Express, Fastify, Next.js, and serverless.

Node.js has no shortage of PDF generation options: Puppeteer, html-pdf, jsPDF, PDFKit. They all have trade-offs. Puppeteer is heavy. html-pdf wraps a deprecated PhantomJS. jsPDF builds PDFs programmatically rather than rendering HTML. PDFKit requires you to build layouts imperatively.

If you have HTML and you want a PDF, the fastest path is a REST API — one fetch call, no extra dependencies, no browser process to manage.

Basic Example with fetch

Node.js 18+ has native fetch. Here's the full working snippet:

generate-pdf.jsjs
const html = `
  <html lang="en">
  <body style="font-family:sans-serif;padding:32px">
    <h1>Hello from Node.js</h1>
    <p>Generated via HTML to PDF API.</p>
  </body>
  </html>
`

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',
    orientation: 'portrait',
  }),
})

if (!res.ok) throw new Error(`API error: ${res.status}`)

const pdfBuffer = Buffer.from(await res.arrayBuffer())

Streaming the PDF in Express

In an Express route, stream the PDF bytes directly to the response:

routes/invoice.jsjs
app.get('/invoice/:id/pdf', async (req, res) => {
  const invoice = await Invoice.findById(req.params.id)
  const html    = renderInvoiceHtml(invoice)

  const apiRes = 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 (!apiRes.ok) return res.status(500).json({ error: 'PDF generation failed' })

  res.set('Content-Type', 'application/pdf')
  res.set('Content-Disposition', `inline; filename="invoice-${invoice.id}.pdf"`)
  apiRes.body.pipe(res)
})

Works Everywhere Node.js Runs

Because this approach is just an HTTP request, it works identically in Next.js API routes, Fastify, serverless functions (Vercel, AWS Lambda, Cloudflare Workers), and any other Node.js environment — no binary, no native module, nothing to configure.