🔧 AI Nachrichten Major AI platforms go down in unprecedented simultaneous outage(03.09.2026 um 17:34 Uhr)
🔧 AI Nachrichten ChatGPT, Claude, and Grok Down? Users Report Widespread Outages(03.09.2026 um 19:14 Uhr)
🔧 AI Nachrichten OpenAI Launches GPT-6 Astra, Says We May Have Entered the AGI Era(03.09.2026 um 22:08 Uhr)
🔧 AI Nachrichten Claude Comes to CarPlay as Fifth Major AI Chatbot App(05.09.2026 um 05:31 Uhr)
🔧 AI Nachrichten OpenAI’s GPT-6 Astra Is AGI, Says NVIDIA CEO Jensen Huang(07.09.2026 um 06:31 Uhr)
🔧 AI Nachrichten Blame AI companies for Mac mini and Mac Studio shortage(31.08.2026 um 10:32 Uhr)
🔧 AI Nachrichten Major AI platforms go down in unprecedented simultaneous outage(03.09.2026 um 17:34 Uhr)
🔧 AI Nachrichten ChatGPT, Claude, and Grok Down? Users Report Widespread Outages(03.09.2026 um 19:14 Uhr)
🔧 AI Nachrichten OpenAI Launches GPT-6 Astra, Says We May Have Entered the AGI Era(03.09.2026 um 22:08 Uhr)
🔧 AI Nachrichten Claude Comes to CarPlay as Fifth Major AI Chatbot App(05.09.2026 um 05:31 Uhr)
🔧 AI Nachrichten OpenAI’s GPT-6 Astra Is AGI, Says NVIDIA CEO Jensen Huang(07.09.2026 um 06:31 Uhr)
🔧 AI Nachrichten Blame AI companies for Mac mini and Mac Studio shortage(31.08.2026 um 10:32 Uhr)

🔧 Programmierung 🕛 kürzlich 4 Min Lesezeit
0

Render invoices to PDF with a GET request instead of shipping Chromium

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Every "generate a PDF invoice" ticket ends the same way: you already have the invoice as an HTML template that looks right in the browser, and now you need bytes you can email or store. The usual answer is Puppeteer, which drags a ~300MB Chromium download into your image, needs a pile of shared libraries on slim base images, and falls over on the first serverless deploy that has a 250MB bundle limit. The alternative is rewriting your invoice layout in a PDF drawing library, which means maintaining the same document twice.



A hosted renderer sidesteps both. You keep the HTML template, and the PDF is one HTTP GET.






One request



SnapPDF takes a public URL and returns PDF bytes:




CODE
curl -o invoice.pdf \
"https://snappdf.dedyn.io/v1/pdf?url=https://example.com"






No headers, no auth dance on the direct base URL, no JSON envelope to unwrap. The response body is the PDF.



Query params, all optional:






































Param Values Default
format
A4, Letter, etc.
A4
landscape
true / false
false
background
true / false
true
scale number 1
wait_for_selector CSS selector none


background matters more than it sounds for invoices. Chromium drops CSS backgrounds when printing by default, so your header bar and zebra-striped line items vanish unless it's on.



wait_for_selector is the one that saves you. If your invoice template fetches line items client-side, the renderer will otherwise happily print an empty table. Have the template set something like <div id="invoice-ready"> once totals are painted, and point the param at #invoice-ready.




CODE
curl -o invoice.pdf \
"https://snappdf.dedyn.io/v1/pdf?url=https%3A%2F%2Fexample.com&format=Letter&background=true&wait_for_selector=%23invoice-ready"









Node, with global fetch



Node 18+ has fetch built in, so there's no dependency at all:




CODE
import { writeFile } from "node:fs/promises";

async function renderPdf(targetUrl, opts = {}) {
const qs = new URLSearchParams({ url: targetUrl, ...opts });
const res = await fetch(`https://snappdf.dedyn.io/v1/pdf?${qs}`);

if (!res.ok) {
throw new Error(`SnapPDF ${res.status}: ${await res.text()}`);
}
const type = res.headers.get("content-type") || "";
if (!type.includes("application/pdf")) {
throw new Error(`Expected a PDF, got ${type}`);
}
return Buffer.from(await res.arrayBuffer());
}

const pdf = await renderPdf("https://example.com", {
format: "Letter",
background: "true",
});
await writeFile("invoice.pdf", pdf);






URLSearchParams handles the encoding, which you want — invoice URLs usually carry a token, and an unencoded & in there silently truncates the target.



The content-type check isn't paranoia. Any failure path that returns a JSON error will otherwise get written to disk as invoice.pdf, and you'll find out when a customer opens it.






What you actually get back



A 200 with Content-Type: application/pdf and the raw file in the body. Nothing to parse, nothing to base64-decode. Treat it as a Buffer (or a stream) and hand it to whatever comes next: S3, a Nodemailer attachment, res.send().



The one constraint worth planning around: the renderer fetches the URL over the public internet, so localhost:3000/invoices/1042 won't work and neither will a page behind your session cookie. The pattern that does work is a signed, short-lived, unauthenticated route.






The thing I'd build with it



An /invoices/:id/pdf endpoint that reuses the template you already have:




CODE
import express from "express";
import crypto from "node:crypto";

const app = express();

// Public, signed, no session required — this is what the renderer loads.
app.get("/render/invoice/:id", (req, res) => {
if (!validSignature(req.params.id, req.query.sig)) return res.sendStatus(403);
res.send(renderInvoiceHtml(req.params.id)); // your existing template
});

app.get("/invoices/:id/pdf", requireAuth, async (req, res) => {
const { id } = req.params;
const sig = crypto
.createHmac("sha256", process.env.RENDER_SECRET)
.update(id)
.digest("hex");

const target = `https://yourapp.com/render/invoice/${id}?sig=${sig}`;
const pdf = await renderPdf(target, {
format: "Letter",
background: "true",
wait_for_selector: "#invoice-ready",
});

res.type("application/pdf")
.set("Content-Disposition", `attachment; filename="invoice-${id}.pdf"`)
.send(pdf);
});






Two routes, one template, zero browser binaries. The HTML invoice stays the source of truth — when finance asks for the tax line to move, you edit CSS and both the web view and the PDF change together.



Same shape works for packing slips, signed quotes, and monthly reports. Anything where you already built the HTML and someone downstream wants a file.



Working examples and the full param list: github.com/clause-netizen/snappdf-api

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
3 Quellen
GPT-6 Astra Release Today? OpenAI’s Next Major AI Model Is Almost Here
1 Quelle
Apple accuses OpenAI of destroying evidence as trade-secrets fight intensifies
1 Quelle
Major AI platforms go down in unprecedented simultaneous outage