Skip to content

Worker-safe session lifecycle over NextPDF Connect

Use a request-scoped session lifecycle in a long-running PHP worker (RoadRunner, Swoole, Laravel Octane). Each request creates its own document session and destroys it after output_pdf. Font, page, and handle state stay inside the worker’s request boundary. The tools are create_pdf, set_font, add_text, and output_pdf — all Core.

Terminal window
composer require nextpdf/server

Bind a transport. This pattern works the same with any transport.

A document_id is an opaque handle scoped to one request. Follow one rule: create per request, destroy per request, never cache or share. With the default destroy: true, output_pdf renders the document and frees the session in one atomic step. The next request on the same worker gets a fresh, independent session. The previous session’s font and content are gone. Each tool result is a normal transport response (PSR-18 §p2). The server code is isolated by the autoload boundary (PSR-4 §3). The session store is the only cross-request state, and it is keyed by id.

ToolRoleRisk tier
create_pdfOpen a per-request sessionSafe
set_fontSet the active font (per session)Caution
add_textWrite contentCaution
output_pdfRender and destroy the sessionApproval Required / Review (base64)

The tool catalog is the source of truth. The tools you can use depend on the installed tier.

Request 1: create_pdfset_fontadd_textoutput_pdf (destroy: true). Request 2 on the same worker starts a new create_pdf session. The old id is now invalid. Set the font again, because it does not carry over.

Wrap the lifecycle so cleanup always runs:

  • Acquire the session when the request starts.
  • Build content.
  • In a finally-equivalent block, call output_pdf, which destroys the session. If you used destroy: false for an inspect-after-output flow, destroy the session explicitly instead.

Never store a document_id in a worker-global, static, or shared container. Never pass it between coroutines, fibers, or request handlers.

  • Reused id after destroy. Returns an unknown-document error. Create a new session per request.
  • Forgotten output_pdf. Memory grows silently until the session TTL expires or the process restarts. Always finalize.
  • Session-limit under load. The store has a concurrent cap. Prompt destruction helps you stay under it.
  • destroy: false without cleanup. Memory grows gradually. Use it only for an explicit inspect-after-output flow, then destroy the session.
  • Shared id across concurrent requests. A race that corrupts the output. Each request owns its own session.

Per-request output is a few KB. The benefit is bounded worker memory across the worker’s lifetime. The profile is structural.

Session isolation is also a confidentiality property. One request’s content never reaches another request, because handles are not shared and the session is destroyed at output.

StatementSpecClausereference_id
Each tool result is a normal transport response.PSR-18§p2
Code is isolated by the autoload class→file mapping.PSR-4§3

Not applicable — all tools are Core.

TransportAvailableNotes
MCP (stdio)YesOne stdio process per worker is typical.
RESTYesThe HTTP request boundary maps to the session boundary.
gRPCYesOne session per RPC sequence.

create_pdf is Safe. set_font and add_text are Caution. output_pdf is Approval Required, downgraded to Review in base64 mode. In a worker, base64 output is the common path and adds no gate (HITL risk tiers).

Base64 worker output:

{ "allowed": true }