Skip to main content

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.

Datos de origen

Repositorio
prisma/prisma-next
Última actividad en el origen
15 de julio de 2026 a las 18:45
Idioma detectado de SKILL.md
inglés
Estrellas
414
Forks
17

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
prisma-next-runtime
description
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.
# 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.ts` for 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` + the `Contract` type from `contract.d.ts`), and any middleware. Exports a `db` value 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 (the `Contract` type from `contract.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 call `await db.connect({ url })`). This is why `db.ts` can 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. `.env` is for per-environment values (`DATABASE_URL`, secrets). The config reads `.env` automatically via `dotenv/config`. Hardcoding `DATABASE_URL` in the config file leaks credentials and bypasses per-env overrides. - **Build-system / dev-server integration is a separate skill.** `vite dev` auto-emit lives in `prisma-next-build`. The runtime side (this skill) reads `contract.json` / `contract.d.ts` regardless 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`): ```typescript // 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 `Contract` from 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_URL` is not set when `db.ts` loads, the factory still returns a client; you can call `await 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: ```typescript // 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: ```typescript // 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 This is the most important rule in this section. `await using db = postgres(...)` disposes when the *enclosing block* exits. In a script module, that block is the module body and disposal fires at process exit — fine. In a request handler, the enclosing block is the handler function, so disposal fires **after every request** — a fresh `pg.Pool` per call, TCP-connect storm, hot loop tearing connections up and down. ```typescript // DO NOT do this — closes the pool after every request. app.get('/users', async (req, res) => { await using db = postgres<Contract>({ contractJson, url: process.env.DATABASE_URL! }); const users = await db.orm.User.all(); res.json(users); }); ``` The right server pattern is a **module-level singleton** in `db.ts`, imported by handlers, never closed during the process lifetime: ```typescript // src/prisma/db.ts — constructed once, lives for the process export const db = postgres<Contract>({ contractJson, url: process.env.DATABASE_URL }); // src/routes/users.ts import { db } from '../prisma/db'; app.get('/users', async (req, res) => { const users = await db.orm.User.all(); res.json(users); }); ``` Servers (HTTP handlers, workers in a request loop) **do not call `db.close()`** at all in steady state. The pool stays open for the process lifetime. `db.close()` and `await using` are for short-lived scripts — `tsx my-script.ts`, Node CLI commands, CI tasks, one-off seed runs — not for code that runs inside a request loop. **Semantics:** - **`close()` is idempotent.** Calling it twice is a no-op. - **`close()` is terminal.** There is no reconnect on a closed `db` — construct a new client if you need another connection. After close, `db.runtime()`, `db.connect(...)`, `db.transaction(...)`, and `db.prepare(...)` reject with `Error('<target> client is closed')` (e.g. `'Postgres client is closed'`, `'SQLite client is closed'`, `'Mongo client is closed'`). - **`close()` does not abort in-flight queries.** `await` outstanding work before calling `close()`. Async iterators from `db.runtime().execute(plan)` and `PreparedStatement` handles held after `close()` fail on their next call. - **Ownership.** `close()` releases only what the façade constructed (`pg.Pool` from `{ url }`, `MongoClient` from `{ url }` / `{ uri, dbName }`, SQLite handle from `{ path }`). If you supplied your own `pg.Pool` / `pg.Client` (Postgres `pg:` option), `mongodb.MongoClient` (Mongo `mongoClient:` option), or a pre-built `binding`, `db.close()` does **not** touch those — you own their lifecycle. **`db.end()` does not exist.** The universal `node-postgres` name is `pool.end()` on a `pg.Pool`; the Prisma Next runtime client is not a `pg.Pool`. The right call is `await db.close()`. ## Workflow — Telemetry middleware The concept: telemetry middleware sees every operation and emits a structured event for each (start, success, error). Pair the events with your observability stack's collector. ```typescript import postgres from '@prisma-next/postgres/runtime'; import { createTelemetryMiddleware } from '@prisma-next/middleware-telemetry'; 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'], middleware: [ createTelemetryMiddleware({ onEvent: (event) => { // forward to your collector, log, etc. }, }), ], }); ``` `createTelemetryMiddleware` is shipped as a separate user-installable package (`@prisma-next/middleware-telemetry`), not as a `/middleware` subpath of the postgres façade. Install it directly. Run `pnpm ls @prisma-next/middleware-telemetry` to confirm it's on the lockfile. ## Workflow — Lints and budgets middleware The concept: lints catch authoring mistakes that survive type-check (e.g. `DELETE` without a `WHERE`, `SELECT` without a `LIMIT` on a large table); budgets enforce row-count and latency ceilings at runtime. Both surface findings through the structured-error envelope so an agent can branch on the code. These ship in the underlying SQL runtime package (`@prisma-next/sql-runtime`) and are *not* yet re-exported from the postgres façade — see *What Prisma Next doesn't do yet*. The example apps under `examples/prisma-next-demo/src/prisma/db.ts` show the canonical import. ```typescript import postgres from '@prisma-next/postgres/runtime'; import { budgets, lints } from '@prisma-next/sql-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'], middleware: [ lints({ severities: { selectStar: 'warn', noLimit: 'error', deleteWithoutWhere: 'error', updateWithoutWhere: 'error', readOnlyMutation: 'error', }, }), budgets({ maxRows: 10_000, defaultTableRows: 10_000, tableRows: { user: 10_000, post: 50_000 }, maxLatencyMs: 1_000, severities: { rowCount: 'error', latency: 'warn' }, }), ], }); ``` For the full option surface, read the source: `packages/2-sql/5-runtime/src/middleware/lints.ts` and `.../budgets.ts`. The `severities` keys (`selectStar`, `noLimit`, `deleteWithoutWhere`, `updateWithoutWhere`, `readOnlyMutation` for lints; `rowCount`, `latency` for budgets) are the source of truth; do not extrapolate to a key that ripgrep can't find. ## Workflow — Compose multiple middleware ```typescript middleware: [ createTelemetryMiddleware({ onEvent }), // outermost — sees all sub-failures as inner errors lints({ severities: { noLimit: 'error' } }), budgets({ maxLatencyMs: 5_000 }), // innermost — runs closest to the driver ], ``` Order matters: outermost wraps. Telemetry first means budget / lint failures are captured as spans (the agent can correlate the lint code with the operation in the same trace). ## Workflow — Configure the connection
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub