Designing an API-first document renderer
The architectural principles behind Tupsara: renderer agnostic, API first, and built so infrastructure decisions stay separate from product decisions.
Tupsara Team
2 min read
There’s a temptation, when you’re building infrastructure, to let the first implementation detail become a permanent architectural commitment. The first rendering engine you pick becomes the rendering engine. The first storage backend becomes load-bearing everywhere. We tried to avoid that with Tupsara by drawing a boundary early between the parts of the system that are product decisions and the parts that are implementation details.
The shape of the system
At a high level, the architecture looks like this:
REST API │ ▼ PDF Application │ ┌────────┴────────┐ ▼ ▼ Renderer Storage │ │ ▼ ▼ Chromium R2The REST API is the only thing a developer needs to think about. Everything below the PDF application layer — which renderer produces the PDF, where the resulting file is stored — is an implementation detail that the application layer owns and can change independently.
Renderer agnostic, on purpose
Chromium is the initial rendering engine, but “renderer” is modeled as an interface inside the PDF application, not as a hardcoded dependency threaded through the codebase. That’s what lets us say, honestly, that Tupsara is designed to support additional rendering engines over time — because the application layer doesn’t know or care which renderer is doing the work, as long as it implements the same contract: HTML and CSS in, a PDF out.
We’re not claiming multi-engine support today. We’re saying the seam for it already exists, because we put it there before writing the Chromium integration, not after.
API first, not API eventually
A lot of infrastructure products build a dashboard first and treat the API as a wrapper around whatever the UI needs. We went the other direction: the REST API is the primary interface, and every capability — rendering options, authentication, request handling — is designed against the API contract first. That ordering matters, because it’s the difference between an API that feels like the product and an API that feels like an afterthought bolted onto one.
What “infrastructure ready” means here, concretely
We use that phrase carefully. It doesn’t mean we’re claiming a specific uptime number or a specific scale we’ve already proven — we’re not going to publish figures we can’t stand behind. What it means is architectural: the system is built with the same separations you’d expect from infrastructure meant to run other people’s production workloads — a stateless API layer, a renderer boundary that can be swapped, and storage that’s decoupled from the application logic instead of tangled into it.
That’s the part we could commit to on day one, before there was any traffic to point to. The rest — proving it at scale — is earned, not architected.