Generate report PDFs in Node.js
The simplest way to generate report PDFs in Node.js is to POST a report template and your metrics JSON to the Docweave API with the built-in fetch — no headless browser to install, launch, or keep patched yourself.
A report template with placeholders
Write plain HTML with {{ placeholders }} for the title, period, summary metrics, and a data table of rows:
<article style="font-family: sans-serif; color: #111;">
<header>
<h1>{{ title }}</h1>
<div>Period: {{ period }}</div>
</header>
<section style="display:flex; gap:24px; margin-top:16px;">
<div>
<div style="font-size:12px; color:#666;">Total revenue</div>
<div style="font-size:20px; font-weight:bold;">{{ revenue }}</div>
</div>
<div>
<div style="font-size:12px; color:#666;">New customers</div>
<div style="font-size:20px; font-weight:bold;">{{ new_customers }}</div>
</div>
<div>
<div style="font-size:12px; color:#666;">Churn rate</div>
<div style="font-size:20px; font-weight:bold;">{{ churn_rate }}</div>
</div>
</section>
<p style="margin-top:16px;">{{ summary }}</p>
<table style="width:100%; border-collapse:collapse; margin-top:16px;">
<thead>
<tr>
<th style="text-align:left; border-bottom:1px solid #ccc;">Metric</th>
<th style="text-align:right; border-bottom:1px solid #ccc;">This period</th>
<th style="text-align:right; border-bottom:1px solid #ccc;">Last period</th>
</tr>
</thead>
<tbody>
{{{ metric_rows }}}
</tbody>
</table>
</article>Generate the report PDF from Node.js
Using only fetch and node:fs/promises (both built into modern Node.js), POST the template and data to /api/v1/pdf and write the raw response bytes to a file:
import { writeFile } from "node:fs/promises";
const template = `<article style="font-family: sans-serif; color: #111;">
<header>
<h1>{{ title }}</h1>
<div>Period: {{ period }}</div>
</header>
<section style="display:flex; gap:24px; margin-top:16px;">
<div>
<div style="font-size:12px; color:#666;">Total revenue</div>
<div style="font-size:20px; font-weight:bold;">{{ revenue }}</div>
</div>
<div>
<div style="font-size:12px; color:#666;">New customers</div>
<div style="font-size:20px; font-weight:bold;">{{ new_customers }}</div>
</div>
<div>
<div style="font-size:12px; color:#666;">Churn rate</div>
<div style="font-size:20px; font-weight:bold;">{{ churn_rate }}</div>
</div>
</section>
<p style="margin-top:16px;">{{ summary }}</p>
<table style="width:100%; border-collapse:collapse; margin-top:16px;">
<thead>
<tr>
<th style="text-align:left; border-bottom:1px solid #ccc;">Metric</th>
<th style="text-align:right; border-bottom:1px solid #ccc;">This period</th>
<th style="text-align:right; border-bottom:1px solid #ccc;">Last period</th>
</tr>
</thead>
<tbody>{{{ metric_rows }}}</tbody>
</table>
</article>`;
const metricRowsHtml = [
{ metric: "MRR", current: "$42,300", previous: "$38,900" },
{ metric: "Active users", current: "6,120", previous: "5,540" },
{ metric: "Support tickets", current: "212", previous: "247" },
]
.map(
(row) =>
`<tr><td>${row.metric}</td><td style="text-align:right;">${row.current}</td><td style="text-align:right;">${row.previous}</td></tr>`
)
.join("");
const response = await fetch("https://docweave.dev/api/v1/pdf", {
method: "POST",
headers: {
Authorization: "Bearer dw_live_your_key",
"Content-Type": "application/json",
Accept: "application/pdf",
},
body: JSON.stringify({
source: {
type: "template",
template,
data: {
title: "Q2 Growth Report",
period: "Apr 1 – Jun 30, 2026",
revenue: "$42,300",
new_customers: "184",
churn_rate: "2.1%",
summary:
"Revenue grew 8.7% quarter over quarter, driven by an uptick in new customer signups and a lower churn rate than Q1.",
metric_rows: metricRowsHtml,
},
},
idempotencyKey: "report-2026-q2",
}),
});
if (!response.ok) {
throw new Error(`Docweave API error: ${response.status} ${await response.text()}`);
}
const pdfBuffer = Buffer.from(await response.arrayBuffer());
await writeFile("q2-growth-report.pdf", pdfBuffer);
console.log("Saved q2-growth-report.pdf");…or from an AI agent
The same report workflow works from an AI agent with no fetch code at all. Run npx @docweave/mcp and your agent gets a generate_pdf tool that wraps the same render orchestration as the REST endpoint:
// From an AI agent over MCP — no fetch/HTTP boilerplate:
// npx @docweave/mcp
generate_pdf({
source: {
type: "template",
template: "<h1>{{ title }}</h1><p>Period: {{ period }}</p><p>{{ summary }}</p>",
data: { title: "Q2 Growth Report", period: "Apr 1 – Jun 30, 2026", summary: "Revenue grew 8.7% quarter over quarter." }
},
outputPath: "/tmp/q2-growth-report.pdf"
})FAQ
How do I generate a report PDF in Node.js without a headless browser?
POST your report template and metrics JSON to the Docweave API with fetch: { source: { type: "template", template, data }, idempotencyKey }. Docweave binds the data into your template's {{ placeholders }} and renders it with Chromium on its own infrastructure, so your Node.js process never launches a browser.
What's the simplest way to fill a report template with data in Node.js?
Write an HTML template with {{ placeholders }} for the title, period, summary metrics, and a data table, then send it with a data object in the same request. No template engine to install — Docweave does the binding server-side.
How do I save the generated report PDF to disk in Node.js?
Request Accept: application/pdf, read the response body with response.arrayBuffer(), wrap it in a Buffer, and write it with node:fs/promises writeFile. The response bytes are the raw PDF — no decoding or base64 step needed.
How do I avoid regenerating the same report PDF twice on a retried request?
Pass an idempotencyKey (a report period slug is a natural choice, e.g. "report-2026-q2"). If a retry sends the same key again, Docweave returns the original PDF instead of re-rendering and creating a second usage_event.
Related
Ready to generate report PDFs from your Node.js app?
Get an API key