Render pipeline
Tracing a POST /templates/:name/render end to end.
sequenceDiagram
participant Client
participant Handler
participant Service as PDFRenderService
participant Engine as Template engine
participant Pool as Browser pool
participant Int as Internal server
participant Browser as Headless browser
Client->>Handler: POST /templates/:name/render {data}
Handler->>Service: RenderPDFWithData
Service->>Service: validate schema (422 on failure)
Service->>Engine: render body/header/footer
Engine-->>Service: HTML + PDF params
Service->>Int: store document → id + URL
Service->>Pool: borrow browser (with retries)
Pool->>Browser: navigate(internal URL)
Browser->>Int: GET HTML + assets
Browser->>Browser: wait lifecycle event
Pool->>Browser: printToPDF
Browser-->>Pool: PDF bytes
Pool-->>Service: PDF
Service-->>Handler: PDF
Handler-->>Client: application/pdfHTTP —
server.Handlers.RenderData(app/server/handlers.go) unescapes the template name, rejects empty bodies, binds the JSON body intomap[string]any, and callspdfRenderService.RenderPDFWithData. Errors are mapped to status codes bybuildError; success writesapplication/pdf.Lookup + validation —
BasePDFRenderService.RenderPDFWithData(app/services/pdf_render_service.go) fetches the template from the store, runs the JSON-schema validator if present (failure →422), then awaitstemplate.Render(ctx, data).HTML render — for an
HTML2PDFTemplate(app/templates/fileloader/html_2_pdf_template.go),Renderschedules work on thefutures.Scheduler. The worker renders body/header/footer via the chosen engine, builds apdfrender.Document, and mapsparams.jsonto apdfrender.Config.Browser render —
cdp.Render.RenderPDF(app/pdfrender/cdp/render.go) applies the render timeout, stores the document in the internal render store (getting a document ID), computes the internal-server URL, builds theprintToPDFparameters, and borrows a browser from the pool with retries. Inside the borrow,printPDFnavigates the page to the internal URL, waits for the configured lifecycle event (or the callback), and callsPrintToPDF.HTML delivery — the browser fetches the stored HTML from the internal callback server; assets resolve against the same server.
Return — the base64 PDF from CDP is decoded and bubbles back up through the futures/await chain to the handler, which sets
Content-Lengthand returns the bytes.
For RenderExample, steps 2–3 pull the example data from the template. For compositions, step 3 fans out: the planner produces a list of tasks, each task’s template is rendered (concurrently), and the resulting PDFs are merged with pdfcpu. PreviewRenderPlan runs the planner and returns the plan as JSON without rendering.
Concurrency note
The futures.PoolScheduler worker count equals the browser pool’s MaxTotal. Increasing render throughput means increasing the pool size, not just the scheduler — the two are wired together in wire_bindings.go.