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/pdf
  1. HTTPserver.Handlers.RenderData (app/server/handlers.go) unescapes the template name, rejects empty bodies, binds the JSON body into map[string]any, and calls pdfRenderService.RenderPDFWithData. Errors are mapped to status codes by buildError; success writes application/pdf.

  2. Lookup + validationBasePDFRenderService.RenderPDFWithData (app/services/pdf_render_service.go) fetches the template from the store, runs the JSON-schema validator if present (failure → 422), then awaits template.Render(ctx, data).

  3. HTML render — for an HTML2PDFTemplate (app/templates/fileloader/html_2_pdf_template.go), Render schedules work on the futures.Scheduler. The worker renders body/header/footer via the chosen engine, builds a pdfrender.Document, and maps params.json to a pdfrender.Config.

  4. Browser rendercdp.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 the printToPDF parameters, and borrows a browser from the pool with retries. Inside the borrow, printPDF navigates the page to the internal URL, waits for the configured lifecycle event (or the callback), and calls PrintToPDF.

  5. HTML delivery — the browser fetches the stored HTML from the internal callback server; assets resolve against the same server.

  6. Return — the base64 PDF from CDP is decoded and bubbles back up through the futures/await chain to the handler, which sets Content-Length and 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.