Prisma Next Runtime
Wire the Prisma Next runtime — `db.ts` setup using `postgres<Contract>(...)` from `@prisma-next/postgres/runtime`, `sqlite<Contract>(...)` from `@prisma-next/sqlite/runtime`, or `mongo<Contract>(...)` from `@prisma-next/mongo/runtime`; middleware composition (telemetry from `@prisma-next/middleware-telemetry`; lints and budgets), `DATABASE_URL` config, per-environment branching, switching between Postgres, SQLite, and Mongo façades. Use for db.ts, postgres(), sqlite(), mongo(), middleware, telemetry, lints, budgets, DATABASE_URL, .env, connection pool, poolOptions, dev vs prod config, transactions, db.transaction, read replicas, multi-database, script won't exit, hangs, close connection, db.end, db.close, pool.end, [Symbol.asyncDispose], await using.
- Skill ID
- prisma/prisma-next/prisma-next-runtime
- Publisher
- prisma
- Repository
- prisma-next
- Installs
- 237
- Files
- 1
- Synced
- Sep 24, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
prisma/prisma-next/prisma-next-runtimeInstalls these files- SKILL.md
What this skill tells the agent
Prisma Next — Runtime (db.ts Wiring)
Edit your data contract. Prisma handles the rest.
This skill covers the runtime entry point — db.ts — and how to compose the database client with extensions, middleware, and environment configuration.
When to Use
- User is wiring up
db.tsfor the first time (post-init). - User wants to add middleware (telemetry, lints, budgets, custom).
- User wants per-environment config (dev vs prod, multi-region).
- User wants to switch between the Postgres, SQLite, and Mongo façades.
- User wants to wrap operations in
db.transaction(...)(Postgres and SQLite). - User is running a one-off script (
tsx my-script.ts, Node CLI, CI task) and the process won't exit after queries finish, or they need script teardown (db.close(),await using). - User mentions: db.ts, postgres(), mongo(), middleware, telemetry, lints, budgets, DATABASE_URL, .env, connection pool, poolOptions, dev vs prod, transactions, read replicas, multi-database, script won't exit, hangs, db.close, db.end, close connection, pool.end, await using.
When Not to Use
- User wants to write queries →
prisma-next-queries. - User is on Supabase — the
supabase()role-first factory,asUser(jwt)/asAnon()/asServiceRole(), JWT config, RLS →prisma-next-supabase. - User wants to edit the contract →
prisma-next-contract. - User wants to wire Prisma Next into a build tool (Vite plugin, Next.js, …) →
prisma-next-build. - User wants to debug a connection / runtime error →
prisma-next-debug. - User wants to file a bug or feature request →
prisma-next-feedback.
Key Concepts
- `db.ts` is the runtime entry point. Imports the runtime factory from the
@prisma-next/<target>façade (@prisma-next/postgres/runtime,@prisma-next/sqlite/runtime, or@prisma-next/mongo/runtime), the contract artefacts (contract.json+ theContracttype fromcontract.d.ts), and any middleware. Exports adbvalue the rest of your app imports. - The façade's runtime factory is the only surface user-authored `db.ts` imports from. Each factory is a default export. For Postgres:
import postgres from '@prisma-next/postgres/runtime'; SQLite:import sqlite from '@prisma-next/sqlite/runtime'; Mongo:import mongo from '@prisma-next/mongo/runtime'. The factory signature is<Target><Contract>(options)— a single type parameter (theContracttype fromcontract.d.ts), and one options object. - Lazy connect. The factory does not connect to the database synchronously. Static query surfaces (
db.sql,db.orm) are available immediately; the driver / pool is instantiated on the first call that needs a runtime (or when you explicitly callawait db.connect({ url })). This is whydb.tscan be imported in modules that load before the env is ready. - Middleware composes in order. The first middleware in the
middleware: [...]array runs outermost — it sees the operation first on the way in and last on the way out. Telemetry first means budget / lint failures show up inside telemetry spans. - `prisma-next.config.ts` vs `.env`. The config (
defineConfig({ contract, db, extensions, migrations })) is for static project shape: contract path, installed extensions, migrations directory, default connection string..envis for per-environment values (DATABASE_URL, secrets). The config reads.envautomatically viadotenv/config. HardcodingDATABASE_URLin the config file leaks credentials and bypasses per-env overrides. - Build-system / dev-server integration is a separate skill.
vite devauto-emit lives inprisma-next-build. The runtime side (this skill) readscontract.json/contract.d.tsregardless of how they got onto disk, so the two skills compose cleanly.
Workflow — Basic db.ts
The concept: db.ts is the seam between the emitted contract artefacts (target-shaped) and the runtime that executes queries against them. Three imports are load-bearing — the runtime factory, the Contract type (so the static query surfaces are typed), and the JSON artefact (so the runtime validates the structure at construct time).
init scaffolds something like this (for --target postgres):
// src/prisma/db.ts
import postgres from '@prisma-next/postgres/runtime';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };
export const db = postgres<Contract>({
contractJson,
url: process.env['DATABASE_URL'],
});(init currently scaffolds at prisma/db.ts instead — see TML-2532 in prisma-next-quickstart. The canonical path is src/prisma/db.ts; the rest of src/ imports from ./prisma/db or ../prisma/db depending on depth.)
Three things to know:
- `<Contract>` type parameter is load-bearing. Without it, the static surfaces collapse to a generic shape and you lose autocomplete on model names. Always import
Contractfrom the emitted./contract.d.ts. - `with { type: 'json' }` is required. Node's ESM JSON-import-attribute spec. Without it, the import errors.
- `url` is optional at construct time. If
DATABASE_URLis not set whendb.tsloads, the factory still returns a client; you can callawait db.connect({ url })later. The factory throws lazily — only when a runtime is actually needed.
The Mongo façade has the same construction shape — import mongo from '@prisma-next/mongo/runtime' — and the same db.connect(...) / db.close() lifecycle methods. The Mongo façade does not expose `db.transaction(...)`. See What Prisma Next doesn't do yet for the workaround. The ORM surface differs in one place: keys. On Mongo, db.orm is keyed by the collection's storage name (from @@map(...), or the lowercased model name if no @@map is set), not by the PSL model name — so model User { … @@map("users") } is reached at db.orm.users, not db.orm.User. The SQL builder lane (db.sql.<table>) doesn't exist on Mongo at all (db.sql is undefined). See prisma-next-queries § MongoDB ORM addressing for the full rule and a rewrite recipe for SQL-target examples.
Workflow — Running as a script (teardown)
The concept: short scripts that connect, query, then expect the process to exit will hang on Postgres because the façade-owned pg.Pool keeps Node's event loop alive. The data round-trip succeeds; the script never exits. Call await db.close() before the script returns (or use await using at the top of a script module so teardown runs when the module exits — see the block-scope warning below for why this matters).
Plain shape — export db from db.ts, import it in the script, close at the end:
// src/scripts/hello.ts
import { db } from '../prisma/db';
const created = await db.orm.User.create({ email: 'alice@example.com', name: 'Alice' });
const read = await db.orm.User.first();
console.log({ created, read });
await db.close();TS 5.2+ idiomatic shape — construct the client at the top of a script module and let [Symbol.asyncDispose] call close() when the module exits:
// src/scripts/hello.ts — top-level await in a script module
import postgres from '@prisma-next/postgres/runtime';
import type { Contract } from '../prisma/contract.d';
import contractJson from '../prisma/contract.json' with { type: 'json' };
await using db = postgres<Contract>({ contractJson, url: process.env.DATABASE_URL! });
const user = await db.orm.User.first();
console.log(user);
// db.close() runs automatically when the script module exits.await using is block-scoped — do not put it inside a request handler
More from prisma
Use this skill in a project
Open any RiverX project, search the Skills panel for prisma/prisma-next/prisma-next-runtime, and install it in one click.
