Pular para o conteúdo
CODECYCLEPDF Services
Como funcionaPreçoPrivacidadeCódigo abertoFAQ
EntrarCriar conta
  1. Início
  2. Guias
  3. HTML para PDF em Node.js

Gerar PDF a partir de HTML em Node.js

Guia · atualizado em 12/09/2026 · 8 min de leitura

Precisa gerar PDF a partir de HTML em Node.js? Faturas, relatórios e ingressos costumam nascer como HTML, porque é o jeito mais rápido de desenhar um documento. Este guia compara as bibliotecas mais usadas no ecossistema JavaScript e mostra o código completo com fetch nativo e Express, usando a API do PDF Services.

  1. As opções no Node.js
  2. Passo a passo com fetch
  3. Exemplo com Express
  4. CSS para PDF
  5. Tratamento de erros
  6. Serverless e containers
  7. Custos e privacidade

As opções no ecossistema Node.js

OpçãoComo funcionaPontos de atenção
PuppeteerControla um Chromium headless e usa page.pdf().Baixa um navegador de centenas de MB, consome bastante memória e exige pacotes especiais em serverless (Lambda, Vercel).
PlaywrightMesma ideia do Puppeteer, com API moderna.A geração de PDF só funciona no Chromium; os mesmos custos de rodar um navegador.
PDFKit, pdfmake, jsPDFConstroem o PDF por código, elemento por elemento.Não convertem HTML de verdade: cada layout precisa ser reescrito na API da biblioteca.
html-pdf (PhantomJS)Wrapper do PhantomJS.PhantomJS está descontinuado há anos. Evite.
API HTTP (PDF Services)Você envia o HTML e recebe o PDF.Depende de rede e é cobrado por uso, mas não há navegador para instalar nem memória extra no servidor.

O PDF Services converte com o iText pdfHTML, um motor maduro de conversão de HTML e CSS para PDF, e todo o código do serviço é aberto sob a licença AGPL v3. Você não precisa instalar o iText, Java ou .NET: a conversão acontece na API.

Se o documento depende de JavaScript rodando na página, como um gráfico desenhado em canvas, um navegador headless é necessário. Para documentos montados no servidor com templates (EJS, Handlebars, Nunjucks, React renderizado para string), uma API de conversão resolve com uma requisição.

Passo a passo com fetch (Node.js 18 ou superior)

1. Crie a conta e a chave

Cadastre a empresa, confirme o e-mail e gere uma chave em Chaves de API no painel. Guarde-a como segredo, em variável de ambiente ou gerenciador de segredos, nunca no repositório.

2. Envie o HTML e salve o PDF

O Node.js 18 já traz fetch, então não é preciso instalar nada. Salve como gerar-pdf.mjs, porque o await no nível do arquivo exige ES modules:

gerar-pdf.mjs
import { writeFile } from 'node:fs/promises';

const apiKey = process.env.PDFSERVICES_API_KEY;
if (!apiKey) throw new Error('Defina a variável PDFSERVICES_API_KEY.');

const html = `<!doctype html>
<html lang="pt-BR">
<head>
  <meta charset="utf-8">
  <style>
    @page { size: A4; margin: 2cm; }
    body { font-family: sans-serif; }
  </style>
</head>
<body><h1>Olá, PDF!</h1></body>
</html>`;

const response = await fetch('https://api.pdfservices.sistema.site/v1/html2pdf/html', {
  method: 'POST',
  headers: { 'X-Api-Key': apiKey, 'Content-Type': 'text/html; charset=utf-8' },
  body: html
});

if (!response.ok) {
  const erro = await response.json().catch(() => ({}));
  throw new Error(`Falha ${response.status}: ${erro.error} - ${erro.message}`);
}

await writeFile('documento.pdf', Buffer.from(await response.arrayBuffer()));
console.log('Cobrado: R$', response.headers.get('x-charged-amount'));

Rode com PDFSERVICES_API_KEY=pdfs_... node gerar-pdf.mjs. Os headers da resposta trazem o valor cobrado (X-Charged-Amount), o saldo restante (X-Balance) e o identificador da transação (X-Transaction-Id).

Nunca chame a API a partir do navegador com a sua chave: ela ficaria visível para qualquer visitante. Faça a chamada no back-end e entregue apenas o PDF ao usuário.

Exemplo com Express: gerando o PDF sob demanda

Aqui a rota monta o HTML, envia no formato JSON e repassa o PDF ao cliente em streaming com Readable.fromWeb, sem guardar o arquivo inteiro na memória:

server.mjs (Express)
import express from 'express';
import { Readable } from 'node:stream';

const app = express();
const API_URL = 'https://api.pdfservices.sistema.site/v1/html2pdf/json';

// Todo dado vindo do usuário deve ser escapado antes de entrar no HTML.
const escapeHtml = (texto) => String(texto).replace(/[&<>"']/g, (c) => (
  { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]
));

app.get('/faturas/:numero/pdf', async (req, res, next) => {
  try {
    const html = `<!doctype html>
      <html lang="pt-BR"><head><meta charset="utf-8"></head>
      <body><h1>Fatura ${escapeHtml(req.params.numero)}</h1></body></html>`;

    const response = await fetch(API_URL, {
      method: 'POST',
      headers: { 'X-Api-Key': process.env.PDFSERVICES_API_KEY, 'Content-Type': 'application/json' },
      body: JSON.stringify({ html })
    });

    if (!response.ok) {
      return res.status(502).json({ erro: `Falha ao gerar o PDF (${response.status}).` });
    }

    res.set('Content-Type', 'application/pdf');
    res.set('Content-Disposition', 'inline; filename="fatura.pdf"');
    // Repassa o PDF em streaming, sem guardá-lo inteiro na memória.
    Readable.fromWeb(response.body).pipe(res);
  } catch (error) {
    next(error);
  }
});

app.listen(3000);

Em projetos maiores, gere o HTML com a engine de templates que você já usa. Deixe o escape automático ligado: é ele que impede que um dado do usuário vire HTML dentro do documento.

CSS para PDF: tamanho da página, cabeçalho, rodapé e quebras

O motor de conversão entende CSS de impressão (paged media). É com ele que você controla o documento, sem nada específico de Node.js:

estilo.css (dentro de uma tag <style>)
@page {
  size: A4;
  margin: 2.5cm 2cm;
  @top-center { content: "Relatório mensal"; }
  @bottom-right { content: "Página " counter(page) " de " counter(pages); }
}

.nova-pagina { page-break-before: always; }

/* Repete o cabeçalho da tabela em todas as páginas */
thead { display: table-header-group; }
  • Tamanho e margens: @page { size: A4; }, A4 landscape, Letter ou medidas como 21cm 29.7cm.
  • Cabeçalho e rodapé: as caixas de margem (@top-center, @bottom-right etc.) com counter(page) e counter(pages) geram o "Página 1 de 3".
  • Quebras: page-break-before, page-break-after e page-break-inside: avoid para não cortar blocos ao meio.
  • Imagens: embutidas como data: URI ou por URL https pública, em JPG ou PNG.
  • Fontes: as padrão já cobrem acentos; para a sua fonte, use @font-face com data: URI. Fontes por URL, como as do Google Fonts, não são baixadas.
  • JavaScript não é executado: monte o HTML final no servidor. Para gráficos, gere uma imagem PNG antes.

Tratamento de erros

Quando algo dá errado, a API responde com um JSON {"error": "...", "message": "..."}. Os casos que valem tratar no código:

HTTPCódigoO que fazer
401invalid_api_keyConfira a chave configurada; ela pode ter sido excluída no painel.
402insufficient_balanceSaldo insuficiente: adicione créditos. Não adianta repetir a chamada.
413payload_too_largeO corpo passou de 1 MB. Reduza imagens embutidas ou use URLs https.
422conversion_failedO HTML não pôde ser convertido. Revise o documento; a falha não é cobrada.
503busyMuitas chamadas simultâneas na mesma conta: repita com espera crescente.

Para novas tentativas, um laço simples com espera crescente (1 s, 2 s, 4 s) resolve. Aplique-o só ao status 503 e a falhas de rede: repetir 401, 402 ou 422 não muda o resultado.

Serverless e containers

Em AWS Lambda, Vercel, Netlify Functions ou Google Cloud Functions, rodar Puppeteer exige pacotes especiais de Chromium, mais memória e cold starts mais longos. Com uma API, a função só faz uma requisição HTTP. O pacote de deploy continua pequeno e o consumo de memória não depende do tamanho do documento.

Custos e privacidade

O PDF Services cobra R$ 0,001 por conversão bem-sucedida, com créditos pré-pagos e sem mensalidade: mil PDFs custam R$ 1,00. Conversões com erro não são cobradas.

O HTML enviado e o PDF gerado são processados só em memória e nunca são armazenados nem registrados em logs. Ficam registrados apenas metadados da transação, como data, tamanho e valor cobrado. Detalhes na Política de Privacidade. Todo o código do serviço é aberto, sob a licença AGPL v3, e pode ser auditado no GitHub.

Gere seu primeiro PDF agora

Crie a conta da sua empresa, gere uma chave no painel e rode o exemplo acima. Pelo painel também dá para testar a conversão antes de escrever uma linha de código.

Criar conta Já tenho conta · Entrar

Outros guias

  • Gerar PDF a partir de HTML em C#
  • Gerar PDF a partir de HTML em Python
  • Referência rápida da API, com exemplos em PHP e cURL

HTML para PDF em C# · HTML para PDF em Node.js · HTML para PDF em Python

Termos de Uso · Política de Privacidade · Código-fonte (GitHub) · © CODECYCLE · Software livre sob AGPL v3