Reportia

Actualizado 5 junio 2026 · 13 min de lectura

Generar PDF con Node.js y JavaScript (API REST sin Puppeteer)

Generar PDFs en Node.js suele significar una de dos cosas: instalar Puppeteer (y con él, un Chromium de 300MB+) o pelear con librerías como PDFKit que te obligan a posicionar cada elemento manualmente. Hay una tercera opción: una API REST donde envías JSON y recibes el PDF.

El problema con Puppeteer y alternativas

SoluciónPesoVelocidadDificultadDocker-friendly
Puppeteer~300MB (Chromium)1-5sMediaComplejo (Chrome deps)
PDFKit~2MB50-200msAlta (layout manual)
jsPDF~500KB100-500msAlta (coordenadas)
wkhtmltopdf~50MB500ms-2sMediaComplejo (Qt deps)
API REST0MB100-300msBaja (solo fetch)Sí (sin deps)

Ejemplo básico: generar un PDF con fetch()

Node.js 18+ incluye fetch() nativo. No necesitas instalar nada:

// generar-pdf.mjs
const response = await fetch("https://reportia.4l3.org/v1/render", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer TU_API_KEY"
  },
  body: JSON.stringify({
    template: "factura-servicios",
    format: "pdf",
    data: {
      folio: "F-2026-0042",
      fecha: "5 de junio de 2026",
      empresa: {
        nombre: "Mi Startup SAS",
        rfc: "MST210101ABC",
        direccion: "Av. Reforma 123, CDMX"
      },
      cliente: {
        nombre: "Acme Corp",
        email: "[email protected]"
      },
      items: [
        { descripcion: "Licencia SaaS mensual", cantidad: 1, precio: "299.00", total: "299.00" },
        { descripcion: "Soporte premium", cantidad: 1, precio: "99.00", total: "99.00" }
      ],
      gran_total: "398.00"
    }
  })
});

const buffer = Buffer.from(await response.arrayBuffer());
const fs = await import("fs");
fs.writeFileSync("factura-F-2026-0042.pdf", buffer);
console.log("PDF generado: factura-F-2026-0042.pdf");

Eso es todo. 30 líneas, cero dependencias, cero Chrome.

Integrar con Express.js (endpoint que sirve PDFs)

Caso común: tu app Express necesita un endpoint GET /facturas/:id/pdf que genera y sirve el PDF al usuario:

import express from "express";

const app = express();

app.get("/facturas/:id/pdf", async (req, res) => {
  // 1. Obtener datos de tu base de datos
  const factura = await db.facturas.findById(req.params.id);
  if (!factura) return res.status(404).json({ error: "Factura no encontrada" });

  // 2. Generar PDF via API
  const pdfResponse = await fetch("https://reportia.4l3.org/v1/render", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.REPORTIA_API_KEY}`
    },
    body: JSON.stringify({
      template: "factura-servicios",
      format: "pdf",
      data: factura
    })
  });

  if (!pdfResponse.ok) {
    return res.status(500).json({ error: "Error generando PDF" });
  }

  // 3. Servir al usuario
  const buffer = Buffer.from(await pdfResponse.arrayBuffer());
  res.setHeader("Content-Type", "application/pdf");
  res.setHeader("Content-Disposition", `inline; filename="factura-${factura.folio}.pdf"`);
  res.send(buffer);
});

app.listen(3000, () => console.log("Server en http://localhost:3000"));

Generar múltiples PDFs en paralelo

Para facturación masiva (fin de mes, lotes de 100+ facturas):

async function generarPDF(factura) {
  const res = await fetch("https://reportia.4l3.org/v1/render", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.REPORTIA_API_KEY}`
    },
    body: JSON.stringify({
      template: "factura-servicios",
      format: "pdf",
      data: factura
    })
  });
  return Buffer.from(await res.arrayBuffer());
}

// Generar 50 facturas en paralelo (lotes de 10)
const facturas = await db.facturas.findPendientes();
const BATCH_SIZE = 10;

for (let i = 0; i < facturas.length; i += BATCH_SIZE) {
  const batch = facturas.slice(i, i + BATCH_SIZE);
  const pdfs = await Promise.all(batch.map(generarPDF));

  // Guardar cada PDF
  pdfs.forEach((buffer, j) => {
    const folio = batch[j].folio;
    fs.writeFileSync(`./output/${folio}.pdf`, buffer);
  });

  console.log(`Lote ${Math.floor(i/BATCH_SIZE) + 1}: ${batch.length} PDFs generados`);
}

console.log(`Total: ${facturas.length} facturas generadas`);

Generar Excel (.xlsx) con el mismo código

Cambia format: "pdf" por format: "xlsx" y obtén un Excel:

const response = await fetch("https://reportia.4l3.org/v1/render", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer TU_API_KEY"
  },
  body: JSON.stringify({
    template: "reporte-ventas",
    format: "xlsx",  // <-- solo cambia esto
    data: {
      titulo: "Ventas Junio 2026",
      filas: salesData
    }
  })
});

const buffer = Buffer.from(await response.arrayBuffer());
fs.writeFileSync("ventas-junio.xlsx", buffer);

Integrar con Next.js (API Route)

// pages/api/factura/[id].js (Next.js)
export default async function handler(req, res) {
  const { id } = req.query;
  const factura = await prisma.factura.findUnique({ where: { id } });

  const pdfRes = await fetch("https://reportia.4l3.org/v1/render", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.REPORTIA_API_KEY}`
    },
    body: JSON.stringify({
      template: "factura-servicios",
      format: "pdf",
      data: factura
    })
  });

  const buffer = Buffer.from(await pdfRes.arrayBuffer());
  res.setHeader("Content-Type", "application/pdf");
  res.send(buffer);
}

Con TypeScript (tipado completo)

interface FacturaData {
  folio: string;
  fecha: string;
  empresa: { nombre: string; rfc: string; direccion: string };
  cliente: { nombre: string; email: string };
  items: Array<{
    descripcion: string;
    cantidad: number;
    precio: string;
    total: string;
  }>;
  gran_total: string;
}

interface RenderRequest {
  template: string;
  format: "pdf" | "xlsx";
  data: FacturaData;
}

async function generarFacturaPDF(data: FacturaData): Promise<Buffer> {
  const body: RenderRequest = {
    template: "factura-servicios",
    format: "pdf",
    data
  };

  const res = await fetch("https://reportia.4l3.org/v1/render", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.REPORTIA_API_KEY!}`
    },
    body: JSON.stringify(body)
  });

  if (!res.ok) throw new Error(`API error: ${res.status}`);
  return Buffer.from(await res.arrayBuffer());
}

Genera tu primer PDF con Node.js gratis

50 documentos/mes en el plan gratuito. Funciona con fetch(), sin Puppeteer.

Empezar gratis →

Preguntas frecuentes

¿Necesito instalar Puppeteer o Chrome para generar PDFs?

No. La API genera el PDF en el servidor. Tu aplicación Node.js solo necesita fetch() para enviar JSON y recibir el binario del PDF. Sin Chrome, sin Puppeteer, sin dependencias pesadas.

¿Puedo generar PDFs desde un frontend (React, Vue, Angular)?

Sí. Puedes llamar a la API desde el frontend con fetch(). Sin embargo, para proteger tu API key, es recomendable hacer la llamada desde tu backend (Express, Next.js API routes, etc.) y servir el PDF al usuario.

¿Cuánto tarda en generar un PDF?

Un documento típico (factura, reporte de 1-5 páginas) se genera en 100-300ms. Documentos más largos (50+ páginas) pueden tardar 1-3 segundos.

¿Funciona con Deno o Bun?

Sí. Cualquier runtime JavaScript con soporte para fetch() funciona. Deno y Bun lo soportan nativamente, al igual que Node.js 18+.