· 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-fetchoppurefetchnativo (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.
Snippet pronti
- Generate:
POST https://backend.quartzapi.com/index.php?r=api/v1-jobs/generate-documentcontemplateCode+data. - Auth:
Authorization: Bearer ${process.env.QUARTZ_API_KEY}. - Download:
result.downloadUrloppureGET …/v1-documents/download&uid=…. - Docs: Documentazione Web API.