Guides Feb 18, 2026 • 8 min read

How to Generate PDF Invoices in Node.js

Generating PDFs from HTML is easy. Running it at scale without your server exploding? That's the hard part. Here is how to do it right.

Whether you're building a SaaS platform, an e-commerce store, or a reporting tool, you will eventually need to generate PDFs. Most developers reach for Puppeteer or Playwright—and for good reason. They are the gold standard for rendering HTML just like a browser does.

The Standard Approach: Puppeteer

Using Puppeteer is straightforward. You launch a headless browser, navigate to a page (or set HTML content), and export to PDF.

const puppeteer = require('puppeteer');

async function generateInvoice() {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  const html = `<h1>Invoice #1234</h1>`;
  await page.setContent(html, { waitUntil: 'networkidle0' });
  
  await page.pdf({
    path: 'invoice.pdf',
    format: 'A4',
    printBackground: true,
  });

  await browser.close();
}

The "Nightmare" in Production

The snippet above works perfectly on your local MacBook. But when you push it to a 1GB RAM VM or a Serverless function, things go south quickly.

Common Scaling Issues:

  • Memory Leaks: Browsers are memory hungry. If a process hangs, it can eat up your entire server's RAM in seconds.
  • Zombie Processes: Failure to properly browser.close() leads to ghost Chromium instances running in the background.
  • Cold Starts: Bundling Chromium (200MB+) in a Lambda function adds seconds of latency.
  • OS Dependencies: Installing the necessary C++ libraries for Chromium on Linux can be a DevOps headache.

Moving to a Managed PDF API

If your business relies on sending invoices to customers, you can't afford a failed process. This is where a managed API like RocketUtils becomes a "Utility Belt" for your code.

Instead of managing Chromium instances, you simply send your HTML to a secure endpoint and get the PDF back. No memory leaks, no DevOps, just results.

const fetch = require('node-fetch');

async function generateInvoice() {
  const response = await fetch('https://rocketutils.dev/v1/pdf', {
    method: 'POST',
    headers: { 'x-api-key': 'your_rocket_key' },
    body: JSON.stringify({
      html: `<h1>Invoice #1234</h1>`,
      options: { format: 'A4', margin: '20px' }
    })
  });

  const pdfBuffer = await response.buffer();
  // Save or stream back to user
}

The Bottom Line

When should you self-host?

  • You have a dedicated DevOps team to monitor browser processes.
  • You have strictly local data that cannot leave your network.
  • Your volume is extremely low and consistency isn't critical.

When should you use RocketUtils?

  • You are running on Fly.io, Vercel, or AWS Lambda with limited resources.
  • You need 99.9% reliability for customer-facing documents.
  • You want to focus on your core business, not fixing libnss3.so errors.

Scale your PDFs effortlessly

Start with our free tier today. No credit card required, instant API key.

Get Started for Free