Inizia gratis

· 8 min di lettura

Generare report PDF in Node.js senza Puppeteer

Niente Puppeteer né Chromium: genera report PDF da Node.js con una chiamata REST a QuartzAPI e servi il buffer da Express.

Puppeteer e le istanze Chrome headless sono noti per consumare molte risorse negli ambienti Node.js. Chromium nei container Docker gonfia le immagini, usa molta RAM (spesso con crash OOM su istanze cloud economiche) e introduce latenza di cold start.

Passando a QuartzAPI elimini del tutto le dipendenze da browser headless. Il servizio Node.js invia il JSON del report via REST; il motore cloud genera il PDF dal template del Visual Builder.

Prerequisiti

  • Node.js v16+ (CommonJS o ES Modules)
  • node-fetch oppure fetch nativo (da Node v18+)
  • API key QuartzAPI e un template attivo (es. SALES_REPORT_Q3)

Fase 1: installa le dipendenze (opzionale)

Con fetch nativo (Node 18+) non servono pacchetti extra per la chiamata HTTP. Su versioni più vecchie:

npm install node-fetch

Express serve solo se esponi una route HTTP che restituisce il PDF (npm install express).

Fase 2: servizio di integrazione API

Crea un modulo dedicato (pdfService.js) che chiama generate-document e poi scarica i byte del PDF. QuartzAPI risponde in JSON con documentId / downloadUrl — non con il PDF grezzo alla prima richiesta.

// pdfService.js
const QUARTZ_GENERATE_URL =
  'https://backend.quartzapi.com/index.php?r=api/v1-jobs/generate-document';
const QUARTZ_DOWNLOAD_URL =
  'https://backend.quartzapi.com/index.php?r=api/v1-documents/download';
const API_KEY = process.env.QUARTZ_API_KEY;

/**
 * Genera un Buffer PDF dai dati del report.
 * @param {string} templateCode - Codice template del Visual Builder.
 * @param {object} reportData - Parametri report (tipicamente master / items).
 * @returns {Promise<Buffer>}
 */
async function generateReportPdf(templateCode, reportData) {
  const generateRes = await fetch(QUARTZ_GENERATE_URL, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      templateCode,
      outputFormat: 'pdf',
      externalId: reportData.externalId || undefined,
      data: reportData,
    }),
  });

  if (!generateRes.ok) {
    const errorText = await generateRes.text();
    throw new Error(`QuartzAPI Error [${generateRes.status}]: ${errorText}`);
  }

  const payload = await generateRes.json();
  const documentId = payload?.result?.documentId;
  const downloadUrl =
    payload?.result?.downloadUrl ||
    `${QUARTZ_DOWNLOAD_URL}&uid=${encodeURIComponent(documentId || '')}`;

  if (!documentId && !payload?.result?.downloadUrl) {
    throw new Error('Risposta QuartzAPI senza documentId / downloadUrl');
  }

  const pdfRes = await fetch(downloadUrl, {
    method: 'GET',
    headers: {
      Authorization: `Bearer ${API_KEY}`,
    },
  });

  if (!pdfRes.ok) {
    const errorText = await pdfRes.text();
    throw new Error(`QuartzAPI download Error [${pdfRes.status}]: ${errorText}`);
  }

  const arrayBuffer = await pdfRes.arrayBuffer();
  return Buffer.from(arrayBuffer);
}

module.exports = { generateReportPdf };

Collega i placeholder del Visual Builder a chiavi come master.report_title e itera le Details su master.items. Approfondimento sulla forma JSON: generare fatture PDF da JSON.

Fase 3: esempio route Express.js

Servi il report generato da una route HTTP Express:

// server.js
const express = require('express');
const { generateReportPdf } = require('./pdfService');

const app = express();

app.get('/reports/monthly-sales', async (req, res) => {
  try {
    const salesData = {
      master: {
        report_title: 'Monthly Sales Analytics - Q3 2026',
        generated_at: new Date().toISOString(),
        total_revenue: '$45,210.00',
        active_subscriptions: 312,
        churn_rate: '1.2%',
        items: [
          { name: 'Enterprise Plan', sales: 120, revenue: '$24,000' },
          { name: 'Pro Plan', sales: 192, revenue: '$21,210' },
        ],
      },
    };

    const pdfBuffer = await generateReportPdf('SALES_REPORT_Q3', salesData);

    res.setHeader('Content-Type', 'application/pdf');
    res.setHeader(
      'Content-Disposition',
      'inline; filename=sales-report.pdf'
    );
    res.send(pdfBuffer);
  } catch (error) {
    console.error('PDF Generation Failed:', error);
    res.status(500).json({ error: 'Failed to generate report' });
  }
});

app.listen(3000, () => console.log('Server running on port 3000'));

Imposta QUARTZ_API_KEY nell’ambiente (mai in chiaro nel codice). Usa Content-Disposition: attachment per forzare il download invece dell’anteprima inline.

Perché abbandonare Puppeteer per un’API?

Metrica Puppeteer / Chrome headless QuartzAPI
Dimensione immagine Docker ~1 GB+ (include Chromium) ~50 MB (Node standard)
Uso RAM ~100–500 MB per render ~2 MB (payload HTTP)
Concorrenza Limitata da CPU/RAM sulla macchina Gestita dalla flotta API
Cold start Avvio Chromium Un round-trip HTTP

Conclusioni

Se il servizio Node.js deve solo fare “JSON in, PDF out”, Puppeteer è quasi sempre lo strumento sbagliato. Mantieni il container leggero, tieni Chromium fuori da Docker e lascia layout e paginazione a QuartzAPI.

Pronto a togliere Puppeteer dalla produzione? Crea un account Beta QuartzAPI gratuito, disegna il template del report una volta e collega pdfService.js alla tua app Express.

👉 Inizia gratis con la Beta QuartzAPI

Snippet pronti

  • Generate: POST https://backend.quartzapi.com/index.php?r=api/v1-jobs/generate-document con templateCode + data.
  • Auth: Authorization: Bearer ${process.env.QUARTZ_API_KEY}.
  • Download: result.downloadUrl oppure GET …/v1-documents/download&uid=….
  • Docs: Documentazione Web API.