prisma-next-runtime
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePrisma Next — Runtime (db.ts
Wiring)
db.tsPrisma Next — 运行时(db.ts
配置)
db.tsEdit your data contract. Prisma handles the rest.
This skill covers the runtime entry point — — and how to compose the database client with extensions, middleware, and environment configuration.
db.ts编辑你的数据契约,其余工作由Prisma处理。
本技能涵盖运行时入口文件————以及如何通过扩展、中间件和环境配置组合数据库客户端。
db.tsWhen to Use
适用场景
- User is wiring up for the first time (post-init).
db.ts - 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 and Mongo façades.
- User wants to wrap operations in .
db.transaction(...) - User is running a one-off script (, Node CLI, CI task) and the process won't exit after queries finish, or they need script teardown (
tsx my-script.ts,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.
- 用户首次配置(初始化后)。
db.ts - 用户想要添加中间件(遥测、代码检查、预算控制、自定义中间件)。
- 用户需要多环境配置(开发/生产环境、多区域)。
- 用户想要在Postgres与Mongo外观层之间切换。
- 用户想要将操作包装在中。
db.transaction(...) - 用户运行一次性脚本(、Node CLI、CI任务),查询完成后进程无法退出,或需要脚本清理(
tsx my-script.ts、db.close())。await using - 用户提及以下内容: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 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
- 用户想要编写查询 → 使用。
prisma-next-queries - 用户想要编辑契约 → 使用。
prisma-next-contract - 用户想要将Prisma Next集成到构建工具(Vite插件、Next.js等)→ 使用。
prisma-next-build - 用户想要调试连接/运行时错误 → 使用。
prisma-next-debug - 用户想要提交Bug或功能请求 → 使用。
prisma-next-feedback
Key Concepts
核心概念
- is the runtime entry point. Imports the runtime factory from the
db.tsfaçade (@prisma-next/<target>or@prisma-next/postgres/runtime), the contract artefacts (@prisma-next/mongo/runtime+ thecontract.jsontype fromContract), and any middleware. Exports acontract.d.tsvalue the rest of your app imports.db - The façade's runtime factory is the only surface user-authored imports from. Postgres:
db.ts. The factory is a default export. The Postgres runtime factory's signature isimport postgres from '@prisma-next/postgres/runtime'— a single type parameter (thepostgres<Contract>(options)type fromContract), and one options object.contract.d.ts - Lazy connect. The factory does not connect to the database synchronously. Static query surfaces (,
db.sql) are available immediately; the driver / pool is instantiated on the first call that needs a runtime (or when you explicitly calldb.orm). This is whyawait db.connect({ url })can be imported in modules that load before the env is ready.db.ts - Middleware composes in order. The first middleware in the 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.
middleware: [...] - vs
prisma-next.config.ts. The config (.env) is for static project shape: contract path, installed extensions, migrations directory, default connection string.defineConfig({ contract, db, extensions, migrations })is for per-environment values (.env, secrets). The config readsDATABASE_URLautomatically via.env. Hardcodingdotenv/configin the config file leaks credentials and bypasses per-env overrides.DATABASE_URL - Build-system / dev-server integration is a separate skill. auto-emit lives in
vite dev. The runtime side (this skill) readsprisma-next-build/contract.jsonregardless of how they got onto disk, so the two skills compose cleanly.contract.d.ts
- 是运行时入口点。从
db.ts外观层(@prisma-next/<target>或@prisma-next/postgres/runtime)导入运行时工厂,导入契约制品(@prisma-next/mongo/runtime+contract.json中的contract.d.ts类型),以及任何中间件。导出Contract供应用程序其他部分导入使用。db - 外观层的运行时工厂是用户编写的唯一需要导入的接口。Postgres:
db.ts。工厂是默认导出。Postgres运行时工厂的签名为import postgres from '@prisma-next/postgres/runtime'——一个类型参数(来自postgres<Contract>(options)的contract.d.ts类型),以及一个选项对象。Contract - 延迟连接。工厂不会同步连接到数据库。静态查询接口(、
db.sql)可立即使用;驱动/连接池会在首次需要运行时的调用中实例化(或当你显式调用db.orm时)。这就是为什么await db.connect({ url })可以在环境变量准备好之前导入到模块中的原因。db.ts - 中间件按顺序组合。数组中的第一个中间件运行在最外层——它在进入时最先看到操作,在退出时最后看到操作。遥测放在最前面意味着预算/代码检查失败会显示在遥测跨度内。
middleware: [...] - 与
prisma-next.config.ts的区别。配置文件(.env)用于静态项目结构:契约路径、已安装的扩展、迁移目录、默认连接字符串。defineConfig({ contract, db, extensions, migrations })用于多环境值(.env、密钥)。配置文件会通过DATABASE_URL自动读取dotenv/config。在配置文件中硬编码.env会泄露凭据并绕过多环境覆盖。DATABASE_URL - 构建系统/开发服务器集成是单独的技能。自动生成功能在
vite dev中。运行时部分(本技能)会读取磁盘上的prisma-next-build/contract.json,无论它们是如何生成的,因此这两个技能可以完美组合。contract.d.ts
Workflow — Basic db.ts
db.ts工作流——基础db.ts
配置
db.tsThe concept: 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 type (so the static query surfaces are typed), and the JSON artefact (so the runtime validates the structure at construct time).
db.tsContractinit--target postgrestypescript
// 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'],
});( currently scaffolds at instead — see TML-2532 in . The canonical path is ; the rest of imports from or depending on depth.)
initprisma/db.tsprisma-next-quickstartsrc/prisma/db.tssrc/./prisma/db../prisma/dbThree things to know:
- 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 emittedContract../contract.d.ts - is required. Node's ESM JSON-import-attribute spec. Without it, the import errors.
with { type: 'json' } - is optional at construct time. If
urlis not set whenDATABASE_URLloads, the factory still returns a client; you can calldb.tslater. The factory throws lazily — only when a runtime is actually needed.await db.connect({ url })
The Mongo façade has the same shape — — and the same surface.
import mongo from '@prisma-next/mongo/runtime'db核心思路:是生成的契约制品(目标特定形状)与执行查询的运行时之间的衔接层。三个导入是必需的——运行时工厂、类型(以便静态查询接口具备类型提示)、JSON制品(以便运行时在构造时验证结构)。
db.tsContractinit--target postgrestypescript
// 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'],
});(目前命令会在生成代码——详见中的TML-2532。标准路径为;下的其他模块根据层级从或导入。)
initprisma/db.tsprisma-next-quickstartsrc/prisma/db.tssrc/./prisma/db../prisma/db需要了解三点:
- 类型参数是必需的。如果没有它,静态接口会退化为通用形状,你将失去模型名称的自动补全功能。务必从生成的
<Contract>中导入./contract.d.ts。Contract - 是必需的。这是Node的ESM JSON导入属性规范。如果没有它,导入会报错。
with { type: 'json' } - 构造时是可选的。如果
url加载时db.ts未设置,工厂仍会返回客户端;你可以稍后调用DATABASE_URL。工厂会延迟抛出错误——仅当实际需要运行时时才会抛出。await db.connect({ url })
Mongo外观层具有相同的结构————以及相同的接口。
import mongo from '@prisma-next/mongo/runtime'dbWorkflow — 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 keeps Node's event loop alive. The data round-trip succeeds; the script never exits. Call before the script returns (or use at the top of a script module so teardown runs when the module exits — see the block-scope warning below for why this matters).
pg.Poolawait db.close()await usingPlain shape — export from , import it in the script, close at the end:
dbdb.tstypescript
// 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 call when the module exits:
[Symbol.asyncDispose]close()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.核心思路:连接、查询后期望进程退出的短脚本在Postgres上会挂起,因为外观层拥有的会保持Node事件循环活跃。数据往返成功,但脚本永远不会退出。在脚本返回前调用(或在脚本模块的顶部使用,以便模块退出时自动执行清理——请参阅下面的块作用域警告了解原因)。
pg.Poolawait db.close()await using基础写法——从导出,在脚本中导入,最后关闭:
db.tsdbtypescript
// 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+ 惯用写法——在脚本模块的顶部构造客户端,让在模块退出时调用:
[Symbol.asyncDispose]close()typescript
// src/scripts/hello.ts — 脚本模块中的顶层await
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()。await using
is block-scoped — do not put it inside a request handler
await usingawait using
是块作用域——不要将其放在请求处理程序内部
await usingThis is the most important rule in this section. 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 per call, TCP-connect storm, hot loop tearing connections up and down.
await using db = postgres(...)pg.Pooltypescript
// 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 , imported by handlers, never closed during the process lifetime:
db.tstypescript
// 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 at all in steady state. The pool stays open for the process lifetime. and are for short-lived scripts — , Node CLI commands, CI tasks, one-off seed runs — not for code that runs inside a request loop.
db.close()db.close()await usingtsx my-script.tsSemantics:
- is idempotent. Calling it twice is a no-op.
close() - is terminal. There is no reconnect on a closed
close()— construct a new client if you need another connection. After close,db,db.runtime(),db.connect(...), anddb.transaction(...)reject withdb.prepare(...)(e.g.Error('<target> client is closed'),'Postgres client is closed','SQLite client is closed').'Mongo client is closed' - does not abort in-flight queries.
close()outstanding work before callingawait. Async iterators fromclose()anddb.runtime().execute(plan)handles held afterPreparedStatementfail on their next call.close() - Ownership. releases only what the façade constructed (
close()frompg.Pool,{ url }fromMongoClient/{ url }, SQLite handle from{ uri, dbName }). If you supplied your own{ path }/pg.Pool(Postgrespg.Clientoption),pg:(Mongomongodb.MongoClientoption), or a pre-builtmongoClient:,bindingdoes not touch those — you own their lifecycle.db.close()
db.end()node-postgrespool.end()pg.Poolpg.Poolawait db.close()这是本节最重要的规则。会在封闭块退出时释放资源。在脚本模块中,该块是模块主体,释放会在进程退出时触发——没问题。在请求处理程序中,封闭块是处理程序函数,因此释放会在每个请求后触发——每次调用都会创建新的,导致TCP连接风暴,频繁创建和销毁连接。
await using db = postgres(...)pg.Pooltypescript
// 不要这样做——每个请求后都会关闭连接池。
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);
});正确的服务器模式是在中创建模块级单例,由处理程序导入,进程运行期间永不关闭:
db.tstypescript
// src/prisma/db.ts — 仅构造一次,伴随进程生命周期
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);
});服务器(HTTP处理程序、请求循环中的工作进程)在稳定状态下完全不调用。连接池会在进程生命周期内保持打开。和适用于短生命周期脚本——、Node CLI命令、CI任务、一次性种子数据运行——不适用于请求循环内运行的代码。
db.close()db.close()await usingtsx my-script.ts语义说明:
- 是幂等的。调用两次不会产生副作用。
close() - 是终结性的。关闭后的
close()无法重新连接——如果需要另一个连接,请构造新的客户端。关闭后,db、db.runtime()、db.connect(...)和db.transaction(...)会抛出db.prepare(...)(例如Error('<target> client is closed')、'Postgres client is closed'、'SQLite client is closed')。'Mongo client is closed' - 不会中止正在进行的查询。调用
close()前请等待未完成的操作完成。close()返回的异步迭代器以及db.runtime().execute(plan)后持有的close()句柄会在下次调用时失败。PreparedStatement - 所有权。仅释放外观层构造的资源(来自
close()的{ url }、来自pg.Pool/{ url }的{ uri, dbName }、来自MongoClient的SQLite句柄)。如果你提供了自己的{ path }/pg.Pool(Postgres的pg.Client选项)、pg:(Mongo的mongodb.MongoClient选项)或预构建的mongoClient:,binding不会触碰这些资源——你需要管理它们的生命周期。db.close()
db.end()node-postgrespg.Poolpool.end()pg.Poolawait 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@prisma-next/middleware-telemetry/middlewarepnpm ls @prisma-next/middleware-telemetry核心思路:遥测中间件会监控每个操作,并为每个操作发出结构化事件(开始、成功、错误)。将事件与你的可观测性堆栈收集器配对使用。
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) => {
// 转发到你的收集器、日志等
},
}),
],
});createTelemetryMiddleware@prisma-next/middleware-telemetry/middlewarepnpm ls @prisma-next/middleware-telemetryWorkflow — Lints and budgets middleware
工作流——代码检查与预算控制中间件
The concept: lints catch authoring mistakes that survive type-check (e.g. without a , without a 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.
DELETEWHERESELECTLIMITThese ship in the underlying SQL runtime package () and are not yet re-exported from the postgres façade — see What Prisma Next doesn't do yet. The example apps under show the canonical import.
@prisma-next/sql-runtimeexamples/prisma-next-demo/src/prisma/db.tstypescript
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',
unindexedPredicate: 'warn',
},
}),
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: and . The keys (, , , , , for lints; , for budgets) are the source of truth; do not extrapolate to a key that ripgrep can't find.
packages/2-sql/5-runtime/src/middleware/lints.ts.../budgets.tsseveritiesselectStarnoLimitdeleteWithoutWhereupdateWithoutWherereadOnlyMutationunindexedPredicaterowCountlatency核心思路:代码检查会捕获类型检查后仍存在的编写错误(例如不带的、大表上不带的);预算控制在运行时强制执行行数和延迟上限。两者都会通过结构化错误包呈现结果,以便代理可以根据代码分支处理。
WHEREDELETELIMITSELECT这些中间件包含在底层SQL运行时包()中,目前尚未从postgres外观层重新导出——请参阅Prisma Next目前不支持的功能。下的示例应用展示了标准导入方式。
@prisma-next/sql-runtimeexamples/prisma-next-demo/src/prisma/db.tstypescript
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',
unindexedPredicate: 'warn',
},
}),
budgets({
maxRows: 10_000,
defaultTableRows: 10_000,
tableRows: { user: 10_000, post: 50_000 },
maxLatencyMs: 1_000,
severities: { rowCount: 'error', latency: 'warn' },
}),
],
});完整的选项接口,请阅读源代码:和。的键(代码检查的、、、、、;预算控制的、)是权威来源;不要推断ripgrep无法找到的键。
packages/2-sql/5-runtime/src/middleware/lints.ts.../budgets.tsseveritiesselectStarnoLimitdeleteWithoutWhereupdateWithoutWherereadOnlyMutationunindexedPredicaterowCountlatencyWorkflow — 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).
typescript
middleware: [
createTelemetryMiddleware({ onEvent }), // 最外层——捕获所有子中间件的错误作为内部错误
lints({ severities: { noLimit: 'error' } }),
budgets({ maxLatencyMs: 5_000 }), // 最内层——最接近驱动运行
],顺序很重要:最外层包裹其他中间件。将遥测放在最前面意味着预算/代码检查失败会被捕获为跨度(代理可以在同一跟踪中将代码检查代码与操作关联起来)。
Workflow — Configure the connection
工作流——配置连接
The concept: the runtime takes one of three binding shapes — , (a pre-constructed or ), or (an explicit kind tag). They're mutually exclusive. The form is for projects that already manage their own pool (e.g. a Lambda layer); is the default. Pool tuning is / — not .
urlpgpg.Poolpg.ClientbindingpgurlpoolOptions.connectionTimeoutMillispoolOptions.idleTimeoutMillisdriverOptionstypescript
// Default — URL string, factory constructs the pool.
postgres<Contract>({
contractJson,
url: process.env['DATABASE_URL'],
poolOptions: {
connectionTimeoutMillis: 20_000,
idleTimeoutMillis: 30_000,
},
});
// BYO pool — pass a pg.Pool you already created.
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env['DATABASE_URL'] });
postgres<Contract>({ contractJson, pg: pool });The and keys are mutually exclusive at the type level; passing both errors.
urlpgDATABASE_URL.envprocess.envdb.ts核心思路:运行时接受三种绑定形状——、(预构造的或)、(显式类型标签)。它们互斥。形式适用于已经管理自己连接池的项目(例如Lambda层);是默认选项。连接池调优使用/——不是。
urlpgpg.Poolpg.ClientbindingpgurlpoolOptions.connectionTimeoutMillispoolOptions.idleTimeoutMillisdriverOptionstypescript
// 默认——URL字符串,工厂构造连接池。
postgres<Contract>({
contractJson,
url: process.env['DATABASE_URL'],
poolOptions: {
connectionTimeoutMillis: 20_000,
idleTimeoutMillis: 30_000,
},
});
// 自带连接池——传入你已创建的pg.Pool。
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env['DATABASE_URL'] });
postgres<Contract>({ contractJson, pg: pool });urlpgDATABASE_URL.envdb.tsprocess.envWorkflow — Per-environment config (dev vs prod)
工作流——多环境配置(开发/生产)
The concept: one per environment; the rest of the shape is the same. For middleware divergence (e.g. strict lints in dev only), branch in on .
DATABASE_URLdb.tsdb.tsprocess.env['NODE_ENV']typescript
const isProd = process.env['NODE_ENV'] === 'production';
export const db = postgres<Contract>({
contractJson,
url: process.env['DATABASE_URL'],
middleware: isProd
? [createTelemetryMiddleware({ onEvent })]
: [
createTelemetryMiddleware({ onEvent }),
lints({ severities: { noLimit: 'error', deleteWithoutWhere: 'error' } }),
],
});.env.env核心思路:每个环境对应一个;的其余结构保持不变。对于中间件差异(例如仅在开发环境启用严格代码检查),在中根据分支处理。
DATABASE_URLdb.tsdb.tsprocess.env['NODE_ENV']typescript
const isProd = process.env['NODE_ENV'] === 'production';
export const db = postgres<Contract>({
contractJson,
url: process.env['DATABASE_URL'],
middleware: isProd
? [createTelemetryMiddleware({ onEvent })]
: [
createTelemetryMiddleware({ onEvent }),
lints({ severities: { noLimit: 'error', deleteWithoutWhere: 'error' } }),
],
});本地使用;生产环境使用部署平台的密钥。永远不要提交文件。
.env.envWorkflow — Transactions
工作流——事务
The concept: opens a transaction, gives the callback a context with the same / surfaces as , and commits on successful return / rolls back on any thrown error. Inside the callback, use and instead of / so the writes ride the transaction.
db.transaction(fn)txsqlormdbtx.sqltx.ormdb.sqldb.ormtypescript
await db.transaction(async (tx) => {
const user = await tx.orm.User.create({ email: 'alice@example.com' });
await tx.orm.Post.create({ userId: user.id, title: 'hello' });
// If either call throws, both inserts roll back.
});The callback returns whatever you return from it — the transaction wrapper passes it through. The object exposes for SQL-builder plans inside the transaction.
txexecute(plan)核心思路:会开启一个事务,向回调函数提供一个上下文,该上下文具有与相同的/接口,并在回调成功返回时提交事务,在抛出任何错误时回滚事务。在回调内部,请使用和而非/,以便写入操作在事务中执行。
db.transaction(fn)txdbsqlormtx.sqltx.ormdb.sqldb.ormtypescript
await db.transaction(async (tx) => {
const user = await tx.orm.User.create({ email: 'alice@example.com' });
await tx.orm.Post.create({ userId: user.id, title: 'hello' });
// 如果任一调用抛出错误,两次插入都会回滚。
});回调会返回你在其中返回的任何值——事务包装器会将其传递出去。对象暴露用于事务内的SQL构建器计划。
txexecute(plan)Workflow — Switch between Postgres and Mongo
工作流——在Postgres与Mongo之间切换
The concept: the façade selection is baked into ( vs ) and (which you import from). To switch a project's target, re-run in the same directory and pick the other target — the init flow detects the existing scaffold and prompts to reinit ( skips the prompt). PN re-scaffolds and for the new façade. The contract source needs to be re-authored for the new target's idioms (Mongo expresses nested documents; Postgres expresses relations).
db.ts@prisma-next/postgres@prisma-next/mongoprisma-next.config.tsdefineConfigprisma-next init--forceprisma-next.config.tsdb.tsAfter the switch:
typescript
// src/prisma/db.ts (Mongo)
import mongo from '@prisma-next/mongo/runtime';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };
export const db = mongo<Contract>({ contractJson, url: process.env['DATABASE_URL'] });The / surfaces stay the same in name; the operators each surface exposes are target-shaped (Mongo has no ).
db.sqldb.ormJOIN核心思路:外观层选择固化在( vs )和(你导入的)中。要切换项目的目标,请在同一目录中重新运行并选择另一个目标——初始化流程会检测到现有脚手架并提示重新初始化(跳过提示)。PN会为新外观层重新生成和。契约源需要针对新目标的特性重新编写(Mongo支持嵌套文档;Postgres支持关联关系)。
db.ts@prisma-next/postgres@prisma-next/mongoprisma-next.config.tsdefineConfigprisma-next init--forceprisma-next.config.tsdb.ts切换后:
typescript
// src/prisma/db.ts (Mongo)
import mongo from '@prisma-next/mongo/runtime';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };
export const db = mongo<Contract>({ contractJson, url: process.env['DATABASE_URL'] });db.sqldb.ormJOINWorkflow — Build-system / dev-server integration
工作流——构建系统/开发服务器集成
If you want contract artefacts to re-emit automatically while the dev server is running (instead of running by hand each time the contract source changes), reach for the build-tool plugin from :
prisma-next contract emitprisma-next-build- Vite: install and register
@prisma-next/vite-plugin-contract-emitinprismaVitePlugin('prisma-next.config.ts').vite.config.ts - Next.js, Webpack, esbuild, Rollup, Turbopack: no first-party plugin yet — the workaround is a script that runs
prebuild. Seeprisma-next contract emitfor the walkthrough.prisma-next-build
The runtime side (this skill) is the same regardless: reads + from disk. The build-system plugin's job is to keep those files current during development.
db.tscontract.jsoncontract.d.ts如果你希望契约制品在开发服务器运行时自动重新生成(而不是每次契约源更改时手动运行),请使用中的构建工具插件:
prisma-next contract emitprisma-next-build- Vite:安装并在
@prisma-next/vite-plugin-contract-emit中注册vite.config.ts。prismaVitePlugin('prisma-next.config.ts') - Next.js、Webpack、esbuild、Rollup、Turbopack:目前没有官方插件——解决方法是添加脚本运行
prebuild。请参阅prisma-next contract emit获取详细步骤。prisma-next-build
运行时部分(本技能)保持不变:从磁盘读取+。构建系统插件的作用是在开发期间保持这些文件更新。
db.tscontract.jsoncontract.d.tsCommon Pitfalls
常见陷阱
- Hardcoding in
DATABASE_URL. Leaks credentials; bypasses per-environment overrides. Useprisma-next.config.ts..env - Omitting the type parameter in
<Contract>. Without it, static surfaces collapse to a generic shape and you lose autocomplete for models. There is no second type parameter — the older two-param signature (postgres<Contract>(...)) is gone.postgres<Contract, TypeMaps> - Forgetting on the contract import. Required by Node's ESM JSON-import-attribute spec.
with { type: 'json' } - Middleware order matters. Outermost wraps. Put telemetry first if you want it to capture inner-middleware errors.
- Importing middleware from a non-existent façade subpath. does not exist. Telemetry comes from
@prisma-next/postgres/middleware; lints / budgets come from@prisma-next/middleware-telemetrytoday (see What Prisma Next doesn't do yet).@prisma-next/sql-runtime - Confabulating lint / budget option names. Lints take (with the six keys above), not
severities/requireWhere. Budgets usemaxRowsWithoutLimit(notmaxLatencyMs) plusmaxDurationMs/maxRows/defaultTableRows. When in doubt, read the source.tableRows - Switching targets without re-emitting. The contract artefacts are target-shaped; emit after the target change.
- Script hangs after queries finish on Postgres. The keeps Node's event loop alive. Solution:
pg.Poolbefore the script returns, orawait db.close()at the top of a script module. Do not putawait using db = postgres<Contract>(...)inside a request handler — it's block-scoped and would close the pool after every request. The right server pattern is a module-level singleton inawait using db = postgres(...)that lives for the process lifetime.db.ts
- 在中硬编码
prisma-next.config.ts。会泄露凭据;绕过多环境覆盖。请使用DATABASE_URL。.env - 在中省略
postgres<Contract>(...)类型参数。没有它,静态接口会退化为通用形状,你将失去模型的自动补全功能。不存在第二个类型参数——旧的双参数签名(<Contract>)已被移除。postgres<Contract, TypeMaps> - 忘记在契约JSON导入中添加。这是Node的ESM JSON导入属性规范要求的。
with { type: 'json' } - 中间件顺序错误。最外层包裹其他中间件。如果你想要遥测捕获内部中间件错误,请将遥测放在最前面。
- 从不存在的外观层子路径导入中间件。不存在。遥测来自
@prisma-next/postgres/middleware;目前代码检查/预算控制来自@prisma-next/middleware-telemetry(请参阅Prisma Next目前不支持的功能)。@prisma-next/sql-runtime - 错误推断代码检查/预算控制选项名称。代码检查接受(包含上述六个键),而非
severities/requireWhere。预算控制使用maxRowsWithoutLimit(而非maxLatencyMs)以及maxDurationMs/maxRows/defaultTableRows。如有疑问,请阅读源代码。tableRows - 切换目标后未重新生成契约。契约制品是目标特定形状的;切换目标后请重新生成。
- Postgres上查询完成后脚本挂起。会保持Node事件循环活跃。解决方法:脚本返回前调用
pg.Pool,或在脚本模块顶部使用await db.close()。不要将await using db = postgres<Contract>(...)放在请求处理程序内部——它是块作用域的,会在每个请求后关闭连接池。正确的服务器模式是在await using db = postgres(...)中创建模块级单例,伴随进程生命周期。db.ts
What Prisma Next doesn't do yet
Prisma Next目前不支持的功能
- subpath. The postgres façade re-exports the runtime factory (
@prisma-next/postgres/middleware), config (./runtime), contract-builder (./config), control (./contract-builder), family (./control), target (./family), and serverless (./target) — but not middleware. Today's workaround: import./serverlessandlintsfrombudgets, and@prisma-next/sql-runtimefromcreateTelemetryMiddleware. Tracked alongside the broader façade-completeness work in Linear@prisma-next/middleware-telemetry; file additional gaps you hit viaTML-2526.prisma-next-feedback - Multi-database routing / read replicas. Prisma Next doesn't ship a built-in primary/replica router or shard-aware client. Workaround: configure separate instances per data store and call the right one in your application code. If you need first-class multi-database routing, file a feature request via the
db.tsskill.prisma-next-feedback - Connection pooling as a first-class config field. and
poolOptions.connectionTimeoutMillisare wired through, but the rest ofpoolOptions.idleTimeoutMillis's tuning surface (max connections,pg.Pool, ssl options, …) is not exposed by name. Workaround: construct theallowExitOnIdleyourself and pass it viapg.Pool. If you need more pool fields surfaced on the façade, file a feature request via thepg:skill.prisma-next-feedback - Query logger middleware as a built-in. Prisma Next doesn't ship a "log every query" middleware. Workaround: write a small custom middleware that wraps each operation and logs; or use and log inside the
createTelemetryMiddlewarecallback. If you need a built-in query log, file a feature request via theonEventskill.prisma-next-feedback
- 子路径。postgres外观层重新导出了运行时工厂(
@prisma-next/postgres/middleware)、配置(./runtime)、契约构建器(./config)、控制(./contract-builder)、家族(./control)、目标(./family)和无服务器(./target)——但不包括中间件。目前的解决方法:从./serverless导入@prisma-next/sql-runtime和lints,从budgets导入@prisma-next/middleware-telemetry。请关注Linear中的createTelemetryMiddleware以了解外观层完整性的相关工作;如果遇到其他问题,请通过TML-2526提交。prisma-next-feedback - 多数据库路由/只读副本。Prisma Next尚未内置主/副本路由器或分片感知客户端。解决方法:为每个数据存储配置单独的实例,并在应用代码中调用正确的实例。如果你需要官方支持多数据库路由,请通过
db.ts提交功能请求。prisma-next-feedback - 连接池作为一等配置字段。和
poolOptions.connectionTimeoutMillis已接入,但poolOptions.idleTimeoutMillis的其他调优接口(最大连接数、pg.Pool、SSL选项等)未通过名称暴露。解决方法:自行构造allowExitOnIdle并通过pg.Pool传入。如果你需要外观层暴露更多连接池字段,请通过pg:提交功能请求。prisma-next-feedback - 内置查询日志中间件。Prisma Next尚未提供“记录所有查询”的中间件。解决方法:编写一个小型自定义中间件包裹每个操作并记录;或使用并在
createTelemetryMiddleware回调中记录。如果你需要内置查询日志,请通过onEvent提交功能请求。prisma-next-feedback
Reference Files
参考文件
This skill is intentionally body-only; , the factory in , the factory in , and the middleware sources in are the authoritative surfaces for option-level detail. When in doubt, read the source.
prisma-next init --helpdefineConfigpackages/3-extensions/postgres/src/config/define-config.tspostgres()packages/3-extensions/postgres/src/runtime/postgres.tspackages/2-sql/5-runtime/src/middleware/{lints,budgets}.ts本技能仅包含主体内容;、中的工厂、中的工厂,以及中的中间件源代码是选项级细节的权威来源。如有疑问,请阅读源代码。
prisma-next init --helppackages/3-extensions/postgres/src/config/define-config.tsdefineConfigpackages/3-extensions/postgres/src/runtime/postgres.tspostgres()packages/2-sql/5-runtime/src/middleware/{lints,budgets}.tsChecklist
检查清单
- imports the runtime factory from
db.tsand the@prisma-next/<target>/runtimetype fromContract../contract.d - on the contract JSON import.
with { type: 'json' } - is the single type parameter on
<Contract>(no second parameter).postgres<Contract>(...) - lives in
DATABASE_URL, not in.env.prisma-next.config.ts - Middleware ordered intentionally (telemetry outermost typically).
- /
lintsuse the verified option keys (budgets,severities,maxLatencyMs,maxRows).tableRows - Per-env divergence (if any) gated by or similar.
NODE_ENV - Did NOT hardcode credentials in any committed file.
- Did NOT confabulate a subpath, a
@prisma-next/postgres/middlewarepackage, or a second type parameter on@prisma-next/postgres-extension-audit.postgres<...> - Did NOT confabulate read-replica / multi-DB / extra pool config — pointed at What Prisma Next doesn't do yet and routed to .
prisma-next-feedback - For build-system / dev-server prompts (Vite plugin, Next.js plugin, …) routed to .
prisma-next-build
- 从
db.ts导入运行时工厂,并从@prisma-next/<target>/runtime导入./contract.d类型。Contract - 契约JSON导入添加了。
with { type: 'json' } - 的唯一类型参数是
postgres<Contract>(...)(无第二个参数)。<Contract> - 存储在
DATABASE_URL中,而非.env。prisma-next.config.ts - 中间件顺序经过合理安排(通常遥测放在最外层)。
- /
lints使用已验证的选项键(budgets、severities、maxLatencyMs、maxRows)。tableRows - 多环境差异(如果有)通过或类似变量控制。
NODE_ENV - 未在任何已提交文件中硬编码凭据。
- 未错误使用子路径、
@prisma-next/postgres/middleware包,或@prisma-next/postgres-extension-audit的第二个类型参数。postgres<...> - 未错误使用只读副本/多数据库/额外连接池配置——已指向Prisma Next目前不支持的功能并引导至。
prisma-next-feedback - 构建系统/开发服务器相关问题(Vite插件、Next.js插件等)已引导至。
prisma-next-build