prisma/prisma
Runtime
Active contributors: jacek-prisma, Oleksii Orlenko, Søren Bramer Schmidt
Purpose
packages/client/src/runtime is the code that generated Prisma Clients import. When a user runs prisma generate, the generator emits a thin per-project wrapper that delegates almost everything here. This page covers ClientEngine (the request orchestrator), the two executors, and the request/response pipeline.
ClientEngine
packages/client/src/runtime/core/engines/client/ClientEngine.ts is the centerpiece of Prisma 7. It owns the lifecycle of a client-side query engine:
- Loading the Wasm query compiler from
@prisma/query-compiler-wasm - Compiling JSON-protocol requests into query plans
- Dispatching plans to a
LocalExecutororRemoteExecutor - Translating engine-level errors into user-facing Prisma errors (
PrismaClientKnownRequestError,PrismaClientUnknownRequestError,PrismaClientInitializationError)
ClientEngine is what getPrismaClient.ts instantiates inside the constructed PrismaClient. There used to be a different engine (a child process that owned a Rust binary); that engine is gone in Prisma 7.
CLIENT_ENGINE_ERROR (a constant inside ClientEngine.ts) raises a PrismaClientInitializationError with code P2038 when no driver adapter is configured. This is the single Prisma 7-only error code: the old engine could fall back to a default connection, the new one cannot.
Executors
Two implementations of Executor live next to ClientEngine:
graph LR
Engine[ClientEngine.request] --> Plan[Compiled query plan]
Plan --> LE[LocalExecutor]
Plan --> RE[RemoteExecutor]
LE --> Interp[QueryInterpreter<br/>client-engine-runtime]
Interp --> Adapter[Driver adapter]
Adapter --> DB[(Database)]
RE --> HTTP[HTTPS to Accelerate]
HTTP --> Cloud[(Prisma Accelerate)]
Cloud --> DBLocalExecutor
Used when the user supplies a SqlDriverAdapterFactory via the adapter constructor option. Hands the compiled query plan to QueryInterpreter from @prisma/client-engine-runtime, which executes it against the adapter's SqlQueryable. This is the path almost all users take.
RemoteExecutor
Used when accelerateUrl is configured. Serializes the query plan and POSTs it to Accelerate, which runs it server-side against a connection pool (using the same QueryInterpreter code, in fact, deployed as query-plan-executor — see that package page).
The shared contract is ExecutePlanParams in packages/client/src/runtime/core/engines/client/Executor.ts. Either executor receives:
- The query plan
- Transaction context (interactive transaction ID, isolation level)
- Tracing context (OTel span linkage)
- Comments to attach to outgoing SQL (SQL Commenter plugins)
Request lifecycle
The full lifecycle from prisma.user.findMany({ ... }) to a returned array of typed objects:
sequenceDiagram
participant User as User code
participant CP as compositeProxy
participant Apply as applyModel/applyFluent
participant RH as RequestHandler
participant DL as DataLoader
participant Engine as ClientEngine
participant Exec as LocalExecutor
User->>CP: prisma.user.findMany({...})
CP->>Apply: invoke action
Apply->>RH: handleRequest(action, args, dataPath)
Note over RH,DL: DataLoader batches concurrent loads
RH->>DL: enqueue(request)
DL->>Engine: request(jsonProtocol)
Engine->>Exec: execute(plan)
Exec-->>Engine: rows
Engine-->>DL: response
DL-->>RH: response
RH-->>Apply: shaped object
Apply-->>CP: typed result
CP-->>User: User[]RequestHandler.ts is the seam where user-facing actions become engine requests. It applies dataPath (the chain of select/include segments accumulated during the fluent walk) to extract the user-visible shape from the engine's response, and renders any errors using core/errorRendering/.
DataLoader.ts is a per-PrismaClient batcher: relation loads issued in the same microtask tick get coalesced into one engine request when possible. This is what makes include-heavy queries efficient.
Fluent API and dataPath
prisma.user.findFirst({...}).posts() is implemented by packages/client/src/runtime/core/model/applyFluent.ts. Each .posts() call appends ['select', <relationName>] to dataPath. At unpack time in RequestHandler.ts, the runtime walks dataPath to extract the relation result.
There's a subtle correctness caveat here, called out in AGENTS.md:
Runtime unpacking in
packages/client/src/runtime/RequestHandler.tscurrently strips'select'/'include'segments beforedeepGet. In extension context resolution,dataPathshould be interpreted as selector/field pairs (select|include, relation field). Do not strip by raw string value or relation fields literally namedselect/includeget dropped.
If you touch dataPath handling, keep this invariant in mind.
Transactions
Two flavors live under core/transaction/:
- Batch transactions —
prisma.$transaction([...]). Sequential or parallel queries with rollback on any failure. - Interactive transactions —
prisma.$transaction(async tx => { ... }). The user holds a transaction handle and can issue queries inside the callback.
Interactive transaction IDs are owned by TransactionManager in client-engine-runtime. Savepoint behavior is provider-specific and dispatched through the adapter's Transaction (createSavepoint/rollbackToSavepoint/releaseSavepoint) rather than synthesized in TypeScript. See client-engine-runtime for details.
Error rendering
packages/client/src/runtime/core/errorRendering/ formats validation and runtime errors. The big test file applyValidationError.test.ts (3,620 lines) is the most reliable source of truth for what the rendering pipeline produces. Use it as fixture inspiration if you change rendering output.
Tracing
packages/client/src/runtime/core/tracing/ integrates with @opentelemetry/api. The companion package @prisma/instrumentation provides the auto-instrumentation entry that wraps PrismaClient operations.
Edge runtime
packages/client/edge.js exports a slimmed-down runtime suitable for edge environments (Cloudflare Workers, Vercel Edge). The web-platform checks live in packages/client-engine-runtime/src/web-platform.ts. Edge users must use a driver adapter — there is no Node-only fallback.
Entry points for modification
- Add a new constructor option: see the five-step list in the parent page
- Change query unpacking:
RequestHandler.tsandapplyFluent.tstogether - Change error mapping:
core/engines/client/ClientEngine.tsandcore/errorRendering/(plus, for driver-side errors,client-engine-runtime/src/user-facing-error.ts) - Add a new action: search for usages in
core/model/and add the dispatch inapplyModel/applyFluent
See also
- Tests — what the package tests cover
client-engine-runtime—QueryInterpreter,TransactionManager, and the user-facing error mapping- Driver adapters — the adapter contract
- Query execution feature — the cross-package narrative
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.