Serving templates
An advanced API, for a server that renders templates by id from JSON data, such as
POST /pdf/invoice. It lives in its own entry, @inkoxide/render/templates, and checks the
data with a zod schema before React renders anything.
import { readFileSync } from 'node:fs';
import { Document, Image, Page, RenderError, Text } from '@inkoxide/render';
import { generate, type TemplateLike } from '@inkoxide/render/templates';
import { z } from 'zod';
const schema = z.object({ number: z.string(), total: z.number() });
const invoice: TemplateLike<typeof schema> = {
meta: { id: 'invoice' },
schema,
Template: ({ data }) => (
<Document>
<Page>
<Image src="logo" className="h-10 w-10" />
<Text>Invoice {data.number}: {data.total}</Text>
</Page>
</Document>
),
images: { logo: readFileSync('logo.png') },
};
const templates = new Map<string, TemplateLike>([[invoice.meta.id, invoice]]);
app.post('/pdf/:id', async (req, res) => {
try {
const template = templates.get(req.params.id);
if (!template) throw new RenderError('UNKNOWN_TEMPLATE', `No template "${req.params.id}".`);
const { pdf } = await generate(template, req.body);
res.type('application/pdf').send(Buffer.from(pdf));
} catch (e) {
if (!(e instanceof RenderError)) throw e;
res.status(e.status).json(e);
}
});
A template is { meta: { id }, schema, Template, images? }: the component receives the parsed
data as data, and images are its static images by id (a logo). An image passed in the render
options under the same id replaces the template's.
generate(template, data, options?)checksdata, renders and resolves to{ pdf, pages, passes, bytes, timings, fonts, warnings, document, images, schemaVersion, engineVersion }.timingshas the milliseconds of each stage, each counted once:reactMs,treeMs(with the rebuilds for the page count),validateMs(0 whenvalidateis off),engineMs,fetchMsandtotalMs.warningsare the document's font fallbacks,hyphenatewith no language, andrenderprops that did not settle; start-up warnings are inengineStatus().documentis the tree that was rendered, andimagesthe images it was rendered with, by id: the tree names an image given as bytes, a file, a URL or a function by an id it made up (image-0,remote-0), andimageshas the bytes behind those ids. The options are those ofrenderToBuffer.parseData(template, data)only checks the data, and returns it parsed, with the schema's defaults applied.renderTree(template, data, options?)builds the document tree without rendering it, for snapshots and previews. Itsdocumentis the tree, and itsimagesthe images it uses. It is synchronous, so it cannot load what a render loads first:await init()before it, and again afterFont.register. Otherwise it throws anINTERNALRenderError that says so.renderSettled(tree, images, options?)renders it, and renders again with the page count whilerenderprops change it;imagesare more images by id ({}when there are none), and it resolves to{ pdf, pages, passes, tree, images, fetchMs, treeMs }(treeMs: the rebuilds for the page count). Pass both the same options.renderDocument(document, images?, options?)renders a document tree that is already built, such as a storedresult.document, and resolves to{ pdf, pages, engineMs }. The document names its images by id, so pass their bytes inimagesor theimagesoption (renderDocument(result.document, result.images)); the other image options do not apply.
Data that does not match the schema fails with INVALID_DATA (400) before anything renders. Its
issues list every problem as { path, message, code }, with readable messages:
{ path: 'items[0].qty', message: 'Missing: expected a number', code: 'invalid_type' }.
UNKNOWN_TEMPLATE (404) is never thrown by the package; it is there for your own lookup, as
above, so that every error has the same shape.
The entry also exports the types of these: TemplateLike (a template), GenerateOptions (the
options of generate), RenderResult (what generate resolves to), Timings (its timings),
TreeResult (what renderTree returns) and RenderOpts (the options of renderSettled and
renderDocument).
Synced from packages/render/GUIDE.md in the inkoxide repository.