prisma-next-migrations
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePrisma Next — Migration Authoring
Prisma Next — 迁移编写
Edit your data contract. Prisma Next plans the migration. You fill in any data transforms.
The three-step user model:
- You edit your data contract. ()
prisma-next-contract - Prisma Next plans the migration for you. ← this skill
- If a data transform is needed, you edit and self-emit. ← this skill
migration.ts
Once the contract changes, you choose how the change reaches the database. This skill covers the two paths ( and + ), the migration-package contract, the authoring API, and the failure modes you recover from without leaving the loop.
db updatemigration planmigratemigration.ts编辑你的数据契约。Prisma Next为你规划迁移。你只需填充必要的数据转换逻辑。
三步用户模型:
- 你编辑数据契约()
prisma-next-contract - Prisma Next为你规划迁移 ← 本技能覆盖内容
- 若需要数据转换,你编辑并自行生成(self-emit) ← 本技能覆盖内容
migration.ts
契约变更后,你可以选择将变更应用到数据库的方式。本技能涵盖两种路径(和 + )、迁移包契约、编写API,以及无需跳出流程即可恢复的故障模式。
db updatemigration planmigratemigration.tsWhen to Use
适用场景
- User edited the contract and wants to apply the change to the DB.
- User wants to author a migration with a data transform.
- User wants to run pending migrations against a local DB.
- User hit ,
MIGRATION.HASH_MISMATCH(unfilled placeholder), or a partially-applied migration.PN-MIG-2001 - User mentions: migrate, migration, db push, db update, ,
prisma migrate dev, drift, hash mismatch, data backfill.prisma migrate deploy
- 用户编辑了契约,想要将变更应用到数据库。
- 用户想要编写包含数据转换的迁移。
- 用户想要在本地数据库上运行待处理的迁移。
- 用户遇到、
MIGRATION.HASH_MISMATCH(未填充占位符)或部分应用的迁移故障。PN-MIG-2001 - 用户提及:migrate、migration、db push、db update、、
prisma migrate dev、drift、hash mismatch、data backfill。prisma migrate deploy
When Not to Use
不适用场景
- User wants to know what migrations will run on deploy / on merge, or to manage refs and invariants → .
prisma-next-migration-review - User wants to edit the contract → .
prisma-next-contract - User wants a deeper read of a single structured error envelope → .
prisma-next-debug
- 用户想了解部署/合并时将运行哪些迁移,或管理引用和不变量 → 使用。
prisma-next-migration-review - 用户想编辑契约 → 使用。
prisma-next-contract - 用户想深入解读单个结构化错误包 → 使用。
prisma-next-debug
Key Concepts
核心概念
- (quick path). Reads the emitted contract, diffs against the live DB, applies the change. Optional
db updateprints the plan without executing. Interactive destructive-op confirmation (or--dry-runto auto-accept). Writes no migration directory. Operations needing data transforms are not handled by this path —-yexcludes thedb updateoperation class entirely and short-circuits where a data transform would be required. Use only against a database that has no shared history with anyone else (your local dev DB).data - (formal path). Reads the emitted contract, diffs against the head of the on-disk migration graph, writes a new migration package under
migration plan. If any operation needs a data transform, the package'smigrations/app/<YYYYMMDDTHHMM>_<snake_slug>/containsmigration.tscalls you fill in.placeholder(...) - The segment in migration paths is the consuming application's contract-space id. Every migration you author lives under
app/. Extensions your contract depends on get their own sibling directories (migrations/app/) — those are managed by the extension package and you don't write into them. Themigrations/<extension-space-id>/segment lands automatically the first time you runapp//migration planagainst an app-level config.db init - Migration package files (inside each ):
migrations/app/<dir>/- — manifest (metadata +
migration.json).migrationHash - — canonical operation list. Content-addressed;
ops.jsonis computed over this.migrationHash - and
end-contract.json— the contract this migration ends at, imported byend-contract.d.tsfor type-safe data transforms.migration.ts - — TypeScript authoring source, framework-rendered by
migration.ts(ormigration plan). You edit specific holes in it (see Fill a placeholder below) and re-emitmigration new/ops.jsonby running it.migration.json
- Self-emit. Running regenerates
node migrations/app/<dir>/migration.tsandops.jsonfrom the (possibly edited) TS source. This is the only supported way to update an existing migration package after edits.migration.json - shape. Framework-rendered. A class extending
migration.ts(re-exported byMigrationin the rendered import line — see the framing block below), with an@prisma-next/target-postgres/migrationgetter that returns an array of factory-call values. The file ends withoperationsso executing it self-emits.MigrationCLI.run(import.meta.url, M) - . A sentinel the planner emits into the rendered
placeholder(slot)(imported frommigration.tson the framework-managed import line) wherever a data transform is needed. Calling@prisma-next/target-postgres/migrationat emit time throwsplaceholder(...)Unfilled migration placeholder. The user replaces thePN-MIG-2001arrow with a real query-plan closure, then self-emits.() => placeholder(...) - . The data-transform factory.
this.dataTransform(endContract, name, { check, run })is a rowset query whose presence-of-any-row signals "work remains";checkis one or more mutation queries that perform the backfill. Both are lazy closures returning query-plans built againstrun. The runner wrapsendContractascheckfor precheck andEXISTS(...)for postcheck, so the same closure asserts both "there is work" and "the work is done".NOT EXISTS(...) - . A boolean field on the JSON result of
pendingPlaceholders.migration planmeans the package was written but contains unfilled placeholders —truewill throwmigrateuntil you editPN-MIG-2001and self-emit.migration.ts - . Content-addressed identity of a migration package.
migrationHashfires when the stored hash inMIGRATION.HASH_MISMATCHdisagrees with the hash recomputed from the on-disk files (almost always: someone editedmigration.jsonwithout self-emitting).migration.ts - Marker. A single row in the table that records "this database is at contract hash X for space Y". Each successful migration advances the marker as part of the same transaction as the DDL.
prisma_contract.markerwrites the marker from the current contract hash, but only after a schema-verification pass succeeds (it will not sign a database whose live schema disagrees with the contract).db sign - Apply runs in a transaction. Each migration runs inside . On any failure mid-migration, Postgres rolls the migration back; the marker stays at the previous migration's
BEGIN ... COMMIThash. The database is not left in a half-applied state for ordinary DDL + data-transform sequences.to - Operation classes. Every operation declares an :
operationClass,additive,widening, ordata. The CLI surfaces these in the plan preview and in JSON output. There is nodestructiveclass and the framework does not emitlong-running— operations stay transactional.CREATE INDEX CONCURRENTLY
- (快速路径):读取生成的契约,与实时数据库对比差异,应用变更。可选
db update参数可打印计划但不执行。对破坏性操作会进行交互式确认(或使用--dry-run自动确认)。不会生成迁移目录。需要数据转换的操作不会通过此路径处理——-y完全排除db update操作类,在需要数据转换时直接终止。仅适用于无共享历史的数据库(如你的本地开发数据库)。data - (正式路径):读取生成的契约,与磁盘上迁移图的最新版本对比差异,在
migration plan下生成新的迁移包。若任何操作需要数据转换,包内的migrations/app/<YYYYMMDDTHHMM>_<snake_slug>/会包含migration.ts调用,需你自行填充。placeholder(...) - 迁移路径中的段是消费应用的契约空间ID:你编写的所有迁移都位于
app/下。你的契约依赖的扩展会有自己的同级目录(migrations/app/)——这些由扩展包管理,你无需写入。首次针对应用级配置运行migrations/<extension-space-id>//migration plan时,db init段会自动生成。app/ - 迁移包文件(每个目录下):
migrations/app/<dir>/- — 清单文件(元数据 +
migration.json)。migrationHash - — 标准操作列表,基于内容寻址;
ops.json通过此文件计算得出。migrationHash - 和
end-contract.json— 迁移最终达成的契约,供end-contract.d.ts导入以实现类型安全的数据转换。migration.ts - — TypeScript编写源文件,由
migration.ts(或migration plan)框架生成。你需要编辑其中特定的空白部分(见下文「填充占位符」),并通过运行该文件重新生成migration new/ops.json。migration.json
- 自行生成(Self-emit):运行会根据(可能已编辑的)TS源文件重新生成
node migrations/app/<dir>/migration.ts和ops.json。这是编辑后更新现有迁移包的唯一支持方式。migration.json - 结构:由框架生成。一个继承自
migration.ts的类(在生成的导入行中从Migration重新导出——见下方框架代码块),包含一个@prisma-next/target-postgres/migrationgetter,返回工厂调用值的数组。文件末尾包含operations,执行时会自行生成文件。MigrationCLI.run(import.meta.url, M) - :当需要数据转换时,规划器会在生成的
placeholder(slot)中插入的标记(从框架管理的导入行中的migration.ts导入)。在生成时调用@prisma-next/target-postgres/migration会抛出placeholder(...)未填充迁移占位符错误。用户需要将PN-MIG-2001箭头函数替换为真实的查询计划闭包,然后自行生成。() => placeholder(...) - :数据转换工厂。
this.dataTransform(endContract, name, { check, run })是一个行集查询,只要存在任何行就表示「仍有工作待完成」;check是一个或多个执行回填的突变查询。两者都是返回基于run构建的查询计划的延迟闭包。运行器会将endContract包装为check用于预检查,包装为EXISTS(...)用于后检查,因此同一个闭包可同时断言「存在待处理工作」和「工作已完成」。NOT EXISTS(...) - :
pendingPlaceholders的JSON结果中的布尔字段。migration plan表示迁移包已生成但包含未填充的占位符——在你编辑true并自行生成之前,migration.ts会抛出migrate错误。PN-MIG-2001 - :迁移包的内容寻址标识。当
migrationHash中存储的哈希与从磁盘文件重新计算的哈希不一致时,会触发migration.json(几乎总是因为有人编辑了MIGRATION.HASH_MISMATCH但未自行生成)。migration.ts - 标记(Marker):表中的单行记录,用于记录「此数据库在空间Y下的契约哈希为X」。每次成功的迁移都会在同一事务中更新标记,作为DDL操作的一部分。
prisma_contract.marker会从当前契约哈希写入标记,但仅在架构验证通过后执行(如果实时架构与契约不一致,不会签署数据库)。db sign - 事务中应用:每个迁移都在事务中运行。迁移过程中任何失败都会触发Postgres回滚;标记会保留在之前迁移的
BEGIN ... COMMIT哈希值。对于普通DDL + 数据转换序列,数据库不会处于部分应用的状态。to - 操作类:每个操作都会声明一个:
operationClass、additive、widening或data。CLI会在计划预览和JSON输出中显示这些类型。没有destructive类,框架也不会生成long-running——操作始终保持事务性。CREATE INDEX CONCURRENTLY
migration.ts
is framework-rendered, not hand-authored
migration.tsmigration.ts
由框架生成,而非手动编写
migration.tsFiles under (for your own app, is always ) are rendered for you by the framework — writes a populated package whenever the contract changes, and writes an empty scaffold when you want to author operations directly. You do not write these files from scratch. You edit specific holes the framework leaves behind — chiefly replacing sentinels with real closures — then self-emit.
migrations/<space-id>/<timestamp>/migration.ts<space-id>app/prisma-next migration planprisma-next migration newplaceholder("<slot>")this.dataTransform({ check, run })The imports at the top of the rendered file currently point at . The user-facing façade does not currently re-export this surface, so the rendered import deliberately reaches into the target package directly. Linear ticket TML-2526 tracks closing that gap; once it lands, the renderer (and this skill) will switch to in one step.
@prisma-next/target-postgres/migration@prisma-next/postgres@prisma-next/postgres/migrationUntil then, treat the rendered import line as framework-managed:
- Leave it where it is. Don't rewrite it to a different path; the framework's renderer is the authoritative shape and any change you make by hand will be reverted (and may trip
@prisma-next/<…>) the next time the package is re-rendered or self-emitted.MIGRATION.HASH_MISMATCH - If you need an additional symbol (e.g. ,
dataTransform,setNotNull) to fill in a placeholder, add it to the existing rendered import line rather than introducing a second import from a differentrawSqlsubpath.@prisma-next/... - The "user code imports only from " convention applies to your own modules (queries, runtime setup, contract authoring). The framework-rendered
@prisma-next/<target>scaffold is the framework's surface, not yours; the rule is suspended for that one file.migration.ts
migrations/<space-id>/<timestamp>/migration.ts<space-id>app/prisma-next migration planprisma-next migration newplaceholder("<slot>")this.dataTransform({ check, run })生成文件顶部的导入目前指向。面向用户的门面目前并未重新导出此接口,因此生成的导入会直接指向目标包。Linear工单TML-2526跟踪此问题的修复;修复完成后,生成器(以及本技能)会一次性切换为。
@prisma-next/target-postgres/migration@prisma-next/postgres@prisma-next/postgres/migration在此之前,请将生成的导入行视为框架管理的内容:
- 保持原样。不要将其重写为其他路径;框架生成器是权威来源,你手动进行的任何更改都会在下次重新生成或自行生成包时被还原(可能会触发
@prisma-next/<…>)。MIGRATION.HASH_MISMATCH - 若需要额外的符号(如、
dataTransform、setNotNull)来填充占位符,请添加到现有的生成导入行中,而非从其他rawSql子路径引入第二个导入。@prisma-next/... - 「用户代码仅从导入」的约定适用于你自己的模块(查询、运行时设置、契约编写)。框架生成的
@prisma-next/<target>脚手架是框架的接口,而非你的;此规则对此文件不适用。migration.ts
Diagnostic codes you route on
需处理的诊断代码
| Code | Source | Move |
|---|---|---|
| Throwing | Open |
| Reading a migration package | The package is malformed. Recover from version control, or run |
| Loading | The file's default export is not a |
| Building a data-transform query plan | The query builder was instantiated with a contract reference different from the |
| | |
| | The marker in |
| Any command needing a marker | The DB has no marker yet. Run |
| 代码 | 来源 | 处理方式 |
|---|---|---|
| 生成时调用 | 打开 |
| 读取迁移包时 | 迁移包已损坏。从版本控制恢复,或运行 |
| 加载 | 文件的默认导出不是 |
| 构建数据转换查询计划时 | 查询构建器使用的契约引用与传递给 |
| | |
| | |
| 任何需要标记的命令 | 数据库尚无标记。运行 |
Decision — which path do you take?
决策——选择哪种路径?
| Situation | Path | Why |
|---|---|---|
| Local dev, schema in flux | | Fast, interactive, no migration files. |
| Shared branch with other developers | | Replayable, reviewable, content-hashed. |
| Anything reaching production | | Production must run a reviewed, hashed migration. |
| Adding a column that needs a backfill | | |
| Recovering from drift (DB diverged from contract) | | Depends on which side is right. See Recover from drift below. |
| 场景 | 路径 | 原因 |
|---|---|---|
| 本地开发,架构频繁变更 | | 快速、交互式,无迁移文件。 |
| 与其他开发者共享分支 | | 可重放、可审核、基于内容哈希。 |
| 任何涉及生产环境的操作 | | 生产环境必须运行经过审核、带哈希的迁移。 |
| 添加需要回填的列 | | |
| 从架构漂移(数据库与契约不一致)中恢复 | 手动修复后运行 | 取决于哪一方是正确的。见下文「从架构漂移中恢复」。 |
Workflow — db update
(quick path)
db update工作流——db update
(快速路径)
db updateThe concept: resolves the destination () against the live DB and applies the difference. Preview with . Destructive ops prompt interactively unless you pass or . The path excludes operations of the class entirely — if the diff requires a data transform, fails with a planning error and you switch to to author the transform.
db updateemitted contract--dry-run-y--no-interactivedatadb updatemigration planRun after a contract edit:
bash
pnpm prisma-next contract emit
pnpm prisma-next db update --db $DATABASE_URL --dry-run
pnpm prisma-next db update --db $DATABASE_URL
pnpm prisma-next db verify --db $DATABASE_URLInspect the JSON output to drive the next move:
bash
pnpm prisma-next db update --db $DATABASE_URL --jsonThe JSON contains with each , plus (in apply mode) and the post-apply . If the command failed because of destructive operations, the error envelope's lists exactly what would have been dropped.
plan.operations[]operationClassexecution.operationsExecutedmarker.storageHashmeta.destructiveOperations[]核心逻辑:将目标(生成的契约)与实时数据库对比,应用差异。使用预览。破坏性操作会进行交互式提示,除非你传递或。此路径完全排除类操作——如果差异需要数据转换,会因规划错误失败,你需要切换到编写转换逻辑。
db update--dry-run-y--no-interactivedatadb updatemigration plan契约编辑后运行:
bash
pnpm prisma-next contract emit
pnpm prisma-next db update --db $DATABASE_URL --dry-run
pnpm prisma-next db update --db $DATABASE_URL
pnpm prisma-next db verify --db $DATABASE_URL查看JSON输出以指导下一步操作:
bash
pnpm prisma-next db update --db $DATABASE_URL --jsonJSON包含,每个操作带有,此外(在应用模式下)还包含和应用后的。如果命令因破坏性操作失败,错误包的会列出将被删除的内容。
plan.operations[]operationClassexecution.operationsExecutedmarker.storageHashmeta.destructiveOperations[]Workflow — migration plan
+ apply
(formal path)
migration planapply工作流——migration plan
+ apply
(正式路径)
migration planapplyThe concept: writes a new migration package on disk. If the planner needed any data transforms, the package is pending — holds calls until you fill them in. runs every pending package in graph order, transactionally.
migration planmigration.tsplaceholder(...)migratePlan a change:
bash
pnpm prisma-next contract emit
pnpm prisma-next migration plan --name <snake_slug>Read the result. The JSON shape exposes the queryable signals:
- — the path of the new package (e.g.
dir).migrations/app/20260515T1200_add_user_email/ - —
pendingPlaceholdersiftruestill containsmigration.tscalls.placeholder(...) - — for spotting
operations[].operationClassanddestructiveops.data - — family-agnostic textual preview.
preview.statements
Inspect the package:
bash
pnpm prisma-next migration show
pnpm prisma-next migration show <dirName-or-migrationHash-prefix>Fill in any data transforms (see Fill a placeholder), self-emit if you edited , then:
migration.tsbash
pnpm prisma-next migrate --db $DATABASE_URL
pnpm prisma-next db verify --db $DATABASE_URLmigratedb updatemigration show核心逻辑:在磁盘上生成新的迁移包。如果规划器需要数据转换,包会处于待处理状态——包含调用,需你填充。会按图顺序事务性地运行所有待处理包。
migration planmigration.tsplaceholder(...)migrate规划变更:
bash
pnpm prisma-next contract emit
pnpm prisma-next migration plan --name <snake_slug>查看结果。JSON结构包含可查询的信号:
- — 新包的路径(例如
dir)。migrations/app/20260515T1200_add_user_email/ - — 如果
pendingPlaceholders仍包含migration.ts调用,则为placeholder(...)。true - — 用于识别
operations[].operationClass和destructive操作。data - — 与数据库无关的文本预览。
preview.statements
查看迁移包:
bash
pnpm prisma-next migration show
pnpm prisma-next migration show <dirName-or-migrationHash-prefix>填充任何数据转换(见「填充占位符」),如果编辑了则自行生成,然后:
migration.tsbash
pnpm prisma-next migrate --db $DATABASE_URL
pnpm prisma-next db verify --db $DATABASE_URLmigratedb updatemigration showWorkflow — Fill a placeholder
工作流——填充占位符
The concept: the planner can detect that a data transform is needed (e.g. backfilling a new column with no default) but not what it should do. It writes a typed scaffold and stops; you fill the check and run closures with real query plans built against , then self-emit.
NOT NULLendContractThe scaffold the planner emits looks like:
typescript
// migrations/app/20260515T1200_add_user_name/migration.ts
import endContract from './end-contract.json' with { type: 'json' };
import { Migration, MigrationCLI, addColumn, placeholder } from '@prisma-next/target-postgres/migration';
export default class M extends Migration {
override get operations() {
return [
addColumn('public', 'user', {
name: 'name',
typeSql: 'text',
defaultSql: '',
nullable: true,
}),
this.dataTransform(endContract, 'backfill user.name', {
check: () => placeholder('backfill user.name:check'),
run: () => placeholder('backfill user.name:run'),
}),
];
}
}
MigrationCLI.run(import.meta.url, M);Replace both calls with query-plan closures built from . The closure must return a rowset query whose presence of any row signals "work remains" — conventionally . Scalar/aggregate shapes (, ) silently break the contract: the runner wraps twice ( for precheck, for postcheck), and a query that always returns one row makes always true and always false.
placeholder(...)endContractcheck<table>.select('id').where(<violation predicate>).limit(1)count(*)bool_and(...)checkEXISTS(...)NOT EXISTS(...)EXISTSNOT EXISTSBuild the query builder against so the storage hashes line up — using a different contract reference raises . The filled-in shape (the rendered scaffold above with calls replaced; if you need an extra factory like , add it to the existing import line rather than authoring a second import). See for the surrounding setup:
endContractPN-MIG-2005placeholder(...)setNotNull@prisma-next/target-postgres/migrationprisma-next-queriesdbtypescript
import endContract from './end-contract.json' with { type: 'json' };
import { Migration, MigrationCLI, addColumn, setNotNull } from '@prisma-next/target-postgres/migration';
import { db } from './db'; // sql({ context: createExecutionContext({ contract: endContract, ... }) })
export default class M extends Migration {
override get operations() {
return [
addColumn('public', 'user', {
name: 'name',
typeSql: 'text',
defaultSql: '',
nullable: true,
}),
this.dataTransform(endContract, 'backfill user.name', {
check: () => db.users.select('id').where((f, fns) => fns.eq(f.name, null)).limit(1),
run: () => db.users.update({ name: '' }).where((f, fns) => fns.eq(f.name, null)),
}),
setNotNull('public', 'user', 'name'),
];
}
}
MigrationCLI.run(import.meta.url, M);Self-emit:
bash
node migrations/app/20260515T1200_add_user_name/migration.tsSelf-emit regenerates and recomputes in . The next will see a consistent package.
ops.jsonmigrationHashmigration.jsonmigrate核心逻辑:规划器可以检测到需要数据转换(例如,回填没有默认值的新列),但无法确定具体如何转换。它会生成一个类型化的脚手架并停止;你需要使用基于构建的真实查询计划填充和闭包,然后自行生成。
NOT NULLendContractcheckrun规划器生成的脚手架如下:
typescript
// migrations/app/20260515T1200_add_user_name/migration.ts
import endContract from './end-contract.json' with { type: 'json' };
import { Migration, MigrationCLI, addColumn, placeholder } from '@prisma-next/target-postgres/migration';
export default class M extends Migration {
override get operations() {
return [
addColumn('public', 'user', {
name: 'name',
typeSql: 'text',
defaultSql: '',
nullable: true,
}),
this.dataTransform(endContract, 'backfill user.name', {
check: () => placeholder('backfill user.name:check'),
run: () => placeholder('backfill user.name:run'),
}),
];
}
}
MigrationCLI.run(import.meta.url, M);将两个调用替换为基于构建的查询计划闭包。闭包必须返回行集查询,只要存在任何行就表示「仍有工作待完成」——通常为。标量/聚合形状(如、)会静默破坏契约:运行器会将包装两次(用于预检查,用于后检查),始终返回一行的查询会导致始终为真,始终为假。
placeholder(...)endContractcheck<table>.select('id').where(<violation predicate>).limit(1)count(*)bool_and(...)checkEXISTS(...)NOT EXISTS(...)EXISTSNOT EXISTS基于构建查询构建器,确保存储哈希一致——使用不同的契约引用会触发。填充后的结构(将上述生成的脚手架中的调用替换;如果需要额外的工厂如,请添加到现有的导入行中,而非引入第二个导入)。有关设置的详细信息,请查看:
endContractPN-MIG-2005placeholder(...)setNotNull@prisma-next/target-postgres/migrationdbprisma-next-queriestypescript
import endContract from './end-contract.json' with { type: 'json' };
import { Migration, MigrationCLI, addColumn, setNotNull } from '@prisma-next/target-postgres/migration';
import { db } from './db'; // sql({ context: createExecutionContext({ contract: endContract, ... }) })
export default class M extends Migration {
override get operations() {
return [
addColumn('public', 'user', {
name: 'name',
typeSql: 'text',
defaultSql: '',
nullable: true,
}),
this.dataTransform(endContract, 'backfill user.name', {
check: () => db.users.select('id').where((f, fns) => fns.eq(f.name, null)).limit(1),
run: () => db.users.update({ name: '' }).where((f, fns) => fns.eq(f.name, null)),
}),
setNotNull('public', 'user', 'name'),
];
}
}
MigrationCLI.run(import.meta.url, M);自行生成:
bash
node migrations/app/20260515T1200_add_user_name/migration.ts自行生成会重新生成并重新计算中的。下次会识别到一致的迁移包。
ops.jsonmigration.jsonmigrationHashmigrateWorkflow — Author a migration by hand
工作流——手动编写迁移
The concept: the same class shape lets you author operations directly when the planner has nothing to plan (a custom data fix, an extension install, a baseline). Even here you don't write the file from scratch — renders an empty package for you, and you edit the getter inside it, then self-emit.
Migrationmigration newoperationsbash
pnpm prisma-next migration new --name <snake_slug>The factories you can call are re-exported through the same framework-rendered import line — add the names you need to that line rather than introducing a second import. Categories (browse with on the command and by reading the import list):
@prisma-next/target-postgres/migration--help- Tables: ,
createTable.dropTable - Columns: ,
addColumn,dropColumn,alterColumnType,setNotNull,dropNotNull,setDefault.dropDefault - Constraints: ,
addPrimaryKey,addForeignKey,addUnique.dropConstraint - Indexes: ,
createIndex.dropIndex - Enums: ,
createEnumType,addEnumValues,renameType.dropEnumType - Dependencies: ,
createSchema,createExtension.installExtension - Raw escape hatch: .
rawSql({ id, label, operationClass, target, precheck, execute, postcheck, ... }) - Data transforms: (instance method, not a free factory).
this.dataTransform(endContract, name, { check, run })
Self-emit () after each edit.
node migrations/app/<dir>/migration.ts核心逻辑:相同的类结构允许你在规划器无内容可规划时直接编写操作(如自定义数据修复、扩展安装、基线设置)。即使在此场景下,你也无需从头编写文件——会为你生成一个空包,你只需编辑其中的 getter,然后自行生成。
Migrationmigration newoperationsbash
pnpm prisma-next migration new --name <snake_slug>你可以调用的工厂函数通过相同的框架生成导入行重新导出——请将所需的名称添加到该行中,而非引入第二个导入。分类(可通过命令的或查看导入列表浏览):
@prisma-next/target-postgres/migration--help- 表:、
createTable。dropTable - 列:、
addColumn、dropColumn、alterColumnType、setNotNull、dropNotNull、setDefault。dropDefault - 约束:、
addPrimaryKey、addForeignKey、addUnique。dropConstraint - 索引:、
createIndex。dropIndex - 枚举:、
createEnumType、addEnumValues、renameType。dropEnumType - 依赖:、
createSchema、createExtension。installExtension - 原生SQL出口:。
rawSql({ id, label, operationClass, target, precheck, execute, postcheck, ... }) - 数据转换:(实例方法,而非自由工厂)。
this.dataTransform(endContract, name, { check, run })
每次编辑后自行生成()。
node migrations/app/<dir>/migration.tsWorkflow — Inspect the live schema
工作流——查看实时架构
The concept: is read-only and never writes files. It prints the live schema as a tree by default or as JSON with . Use it during planning and as part of verification.
db schema--jsonbash
pnpm prisma-next db schema --db $DATABASE_URL
pnpm prisma-next db schema --db $DATABASE_URL --json > schema.jsonThere is no built-in filter flag — pipe the JSON through (or your favourite JSON tool) if you only want one table.
jq核心逻辑:是只读命令,不会写入文件。默认以树形结构打印实时架构,使用参数可输出JSON格式。可在规划和验证阶段使用。
db schema--jsonbash
pnpm prisma-next db schema --db $DATABASE_URL
pnpm prisma-next db schema --db $DATABASE_URL --json > schema.json没有内置的过滤标志——如果只需要单个表,请将JSON通过(或你喜欢的JSON工具)处理。
jqWorkflow — Verify contract vs DB
工作流——验证契约与数据库
The concept: compares the live schema and the marker against the contract. Three modes:
db verify- Default — full verification (schema + marker).
- — skip schema verification, only check the marker.
--marker-only - — skip marker verification, only check schema satisfies contract.
--schema-only - adds: schema elements not present in the contract are an error (default is "DB may have extras").
--strict
bash
pnpm prisma-next db verify --db $DATABASE_URLOn mismatch, the error envelope names the failure mode ( hash mismatch, marker missing, target mismatch, schema issues with structured paths). Run after any manual fix or any .
PN-RUN-3002PN-RUN-3001apply核心逻辑:会将实时架构和标记与契约进行对比。三种模式:
db verify- 默认 — 完整验证(架构 + 标记)。
- — 跳过架构验证,仅检查标记。
--marker-only - — 跳过标记验证,仅检查架构是否符合契约。
--schema-only - 模式新增:架构中存在契约未包含的元素时视为错误(默认是「数据库可以有额外元素」)。
--strict
bash
pnpm prisma-next db verify --db $DATABASE_URL不匹配时,错误包会指明失败模式(哈希不匹配、标记缺失、目标不匹配、带结构化路径的架构问题)。在任何手动修复或操作后运行此命令。
PN-RUN-3002PN-RUN-3001applyWorkflow — Re-sign the marker
工作流——重新签署标记
The concept: rewrites the marker to the current contract hash. Use after a manual repair where the DB is the source of truth and the marker is stale. performs a schema-verify first and refuses to sign a DB whose schema disagrees with the contract — so a successful sign always means the schema matches and the marker is now correct.
db signdb signbash
pnpm prisma-next db sign --db $DATABASE_URL核心逻辑:会将标记重写为当前契约哈希。适用于手动修复后,数据库为可信源但标记过期的场景。会先执行架构验证,拒绝签署架构与契约不一致的数据库——因此成功签署意味着架构匹配且标记已正确更新。
db signdb signbash
pnpm prisma-next db sign --db $DATABASE_URLWorkflow — Recover from drift
工作流——从架构漂移中恢复
The concept: drift means reports the live DB schema doesn't match what the marker says it should be. Two valid moves, picked by which side is correct:
db verify- The contract is right; the DB is wrong → run a migration. Either (quick path, dev DB only) or
db update+migration plan(everywhere else).apply - The DB is right; the contract or marker is wrong → edit the contract to match the DB (see ), emit, then
prisma-next-contractto refresh the marker.db sign
The diagnostic that reveals which side is right:
bash
pnpm prisma-next db schema --db $DATABASE_URL --json
pnpm prisma-next db verify --db $DATABASE_URL --jsonRe-verify after either branch. Stop when returns with no diagnostics.
db verifyok核心逻辑:架构漂移指报告实时数据库架构与标记所示的预期架构不一致。有两种有效操作,取决于哪一方是正确的:
db verify- 契约正确;数据库错误 → 运行迁移。可选择(快速路径,仅适用于开发数据库)或
db update+migration plan(其他场景)。apply - 数据库正确;契约或标记错误 → 编辑契约以匹配数据库(见),生成契约,然后运行
prisma-next-contract刷新标记。db sign
可通过以下命令判断哪一方正确:
bash
pnpm prisma-next db schema --db $DATABASE_URL --json
pnpm prisma-next db verify --db $DATABASE_URL --json无论选择哪种分支,恢复后都需重新验证。直到返回且无诊断信息为止。
db verifyokWorkflow — Recover from a partially-applied migration
工作流——从部分应用的迁移中恢复
The concept: each migration applies transactionally, so a mid-migration failure rolls back to the previous successful migration. The marker stays at the previous migration's hash. The diagnostic move is to verify state, fix the failed migration's , self-emit, and re-apply.
tomigration.tsFailures that can leak partial state are limited to: explicit operations that step outside a transaction (e.g. inside an step that runs against an autocommit driver session), or external side-effects (calls out to other systems from a closure). For ordinary DDL + flows the runner's transaction wrap keeps the database consistent.
rawSql(...)executerunthis.dataTransform(...)Diagnose:
bash
pnpm prisma-next db verify --db $DATABASE_URL --json
pnpm prisma-next db schema --db $DATABASE_URL --jsonFix and re-apply:
bash
node migrations/app/<dir>/migration.ts
pnpm prisma-next migrate --db $DATABASE_URLIf the failure was an out-of-band side-effect that left external systems half-changed, repair those by hand before re-applying.
核心逻辑:每个迁移都在事务中应用,因此迁移过程中的失败会回滚到上一次成功的迁移状态。标记会保留在上一次迁移的哈希值。诊断步骤是验证状态,修复失败迁移的,自行生成,然后重新应用。
tomigration.ts可能导致部分状态泄露的失败仅限于:显式的操作跳出事务(例如,在针对自动提交驱动会话运行的步骤中),或外部副作用(从闭包调用其他系统)。对于普通DDL + 流程,运行器的事务包装会保持数据库一致性。
rawSql(...)executerunthis.dataTransform(...)诊断:
bash
pnpm prisma-next db verify --db $DATABASE_URL --json
pnpm prisma-next db schema --db $DATABASE_URL --json修复并重新应用:
bash
node migrations/app/<dir>/migration.ts
pnpm prisma-next migrate --db $DATABASE_URL如果失败是由外部系统的副作用导致其处于半变更状态,请在重新应用前手动修复这些系统。
Workflow — Recover from MIGRATION.HASH_MISMATCH
MIGRATION.HASH_MISMATCH工作流——从MIGRATION.HASH_MISMATCH
中恢复
MIGRATION.HASH_MISMATCHThe concept: is content-addressed. A mismatch means 's stored hash disagrees with the hash recomputed from (and metadata). The cause is almost always: someone edited and forgot to self-emit. The remediation is to self-emit the offending package.
migrationHashmigration.jsonops.jsonmigration.tsbash
node migrations/app/<dir>/migration.ts
pnpm prisma-next migrate --db $DATABASE_URLIf self-emit itself fails (e.g. the contract has moved on and the operations no longer make sense against ), the package is stale. Either restore it from version control or delete it and re-plan with .
end-contract.jsonmigration plan核心逻辑:是基于内容寻址的。不匹配意味着中存储的哈希与从(和元数据)重新计算的哈希不一致。原因几乎总是:有人编辑了但忘记自行生成。修复方法是自行生成有问题的迁移包。
migrationHashmigration.jsonops.jsonmigration.tsbash
node migrations/app/<dir>/migration.ts
pnpm prisma-next migrate --db $DATABASE_URL如果自行生成本身失败(例如,契约已变更,操作不再与匹配),则迁移包已过期。可从版本控制恢复,或删除后重新运行规划。
end-contract.jsonmigration planWorkflow — Resolve a destructive-operation prompt (db update
only)
db update工作流——解决破坏性操作提示(仅db update
)
db updateThe concept: when would drop columns or tables, it stops and asks before applying. The prompt is -specific — does not prompt and runs whatever the migration package contains, so review the plan or call before applying.
db updatedb updatemigratemigration showWhen reports destructive operations interactively, the warning lists them. The prompt is:
db updateApply destructive changes? This cannot be undone.
Routing:
- Answer yes if the data is no longer needed.
- Answer no, then either:
- Re-shape the migration via and hand-edit
migration planto preserve the data (e.g. copy-to-new-column, then drop), ormigration.ts - Skip the destructive operation by reverting the contract change.
- Re-shape the migration via
In non-interactive contexts (CI, , ), the destructive-op response is returned as a structured error — lists what would have been dropped. Re-run with to auto-accept, or address each operation individually.
--no-interactive--jsonmeta.destructiveOperations[]-y核心逻辑:当将删除列或表时,会停止并询问是否继续。此提示仅针对——不会提示,会直接运行迁移包中的所有操作,因此在应用前请查看计划或调用。
db updatedb updatemigratemigration show当交互式报告破坏性操作时,警告会列出这些操作。提示内容为:
db update是否应用破坏性变更?此操作不可撤销。
处理方式:
- 如果数据不再需要,选择是。
- 如果选择否,可:
- 通过重新规划迁移,并手动编辑
migration plan以保留数据(例如,复制到新列,然后删除旧列),或migration.ts - 回滚契约变更以跳过破坏性操作。
- 通过
在非交互式环境(CI、、)中,破坏性操作的响应会作为结构化错误返回——列出将被删除的内容。可添加参数重新运行以自动确认,或逐个处理每个操作。
--no-interactive--jsonmeta.destructiveOperations[]-yCommon Pitfalls
常见陷阱
- Using against shared or production databases. Never. The change leaves no migration history. Use
db update+migration plan.apply - Skipping a data transform. Leaving in
placeholder(...)makes the nextmigration.tsthrowmigrate. Fill every placeholder slot and self-emit.PN-MIG-2001 - Editing directly. It's the canonical artifact, not the authoring source. Edit
ops.json, then self-emit.migration.ts - Forgetting to self-emit after editing . The next
migration.tseither uses the staleapply(if you only added comments) or fails withops.json(if you changed operations). Always self-emit.MIGRATION.HASH_MISMATCH - Aggregate closure in
check. Returningthis.dataTransformorcount(*)breaks the precheck/postcheck contract — both sides resolve to constants. Use a rowset shape:bool_and(...).select('id').where(<violation>).limit(1) - Two contract references in one migration. Building a query plan against a different contract than the one passed to raises
this.dataTransform(endContract, ...). Always importPN-MIG-2005once at module scope and use the same reference.endContract - Renaming and expecting the planner to detect it. Prisma Next has no in-contract rename hint today; the planner emits a destructive drop+add. Hand-edit to rewrite the destructive op as a
migration.tsthat issuesrawSql({ ... })(or use the two-migration keep / backfill / drop pattern), then self-emit. SeeALTER TABLE ... RENAME COLUMN ...§ Edit a field — rename.prisma-next-contract - Hand-authoring from a blank file, or rewriting the rendered import line. Migration files are framework-rendered — let
migration.ts(orprisma-next migration plan) render the package, then edit only the holes the framework leaves for you. The renderedmigration newimport is the framework's surface, not a stable user-facing one (TML-2526 tracks moving it to@prisma-next/target-postgres/migration); leave the path alone, and add any extra symbols you need to the existing import line.@prisma-next/postgres/migration
- 对共享或生产数据库使用:绝对禁止。此变更不会留下迁移历史。请使用
db update+migration plan。apply - 跳过数据转换:中保留
migration.ts会导致下次placeholder(...)抛出migrate错误。请填充所有占位符并自行生成。PN-MIG-2001 - 直接编辑:这是标准工件,而非编写源文件。请编辑
ops.json,然后自行生成。migration.ts - 编辑后忘记自行生成:下次
migration.ts要么使用过期的apply(如果仅添加了注释),要么因ops.json失败(如果更改了操作)。请务必自行生成。MIGRATION.HASH_MISMATCH - 中使用聚合
this.dataTransform闭包:返回check或count(*)会破坏预检查/后检查契约——两者都会解析为常量。请使用行集形状:bool_and(...)。select('id').where(<violation>).limit(1) - 单个迁移中使用两个契约引用:基于与不同的契约构建查询计划会触发
this.dataTransform(endContract, ...)。请始终在模块作用域导入一次PN-MIG-2005并使用相同的引用。endContract - 重命名字段并期望规划器自动检测:Prisma Next目前没有契约内的重命名提示;规划器会生成破坏性的删除+添加操作。请手动编辑,通过
migration.ts执行rawSql({ ... })(或在两次迁移中使用保留/回填/删除模式),然后自行生成。详见ALTER TABLE ... RENAME COLUMN ...§ 编辑字段——重命名。prisma-next-contract - 从空白文件手动编写,或重写生成的导入行:迁移文件由框架生成——请让
migration.ts(或prisma-next migration plan)生成包,然后仅编辑框架留下的空白部分。生成的migration new导入是框架的接口,而非稳定的用户接口(TML-2526跟踪将其迁移到@prisma-next/target-postgres/migration);请保持路径不变,将所需的额外符号添加到现有导入行中。@prisma-next/postgres/migration
What Prisma Next doesn't do yet
Prisma Next目前未支持的功能
- Runtime-apply migrations. Prisma Next doesn't apply pending migrations from your app's startup code (the "Drizzle pattern" for serverless / edge). Workaround: run from your deploy pipeline before the app starts. If you need runtime-apply built-in, file a feature request via the
prisma-next migrateskill.prisma-next-feedback - Seeds-as-first-class. Prisma Next doesn't ship a equivalent. Workaround: write a TypeScript script that imports your
prisma db seedinstance and runs your setup queries; invoke it fromdb's scripts. If you need first-class seeding, file a feature request via thepackage.jsonskill.prisma-next-feedback - Migration squashing. Prisma Next doesn't squash older migrations into a baseline. They accumulate; for very large histories, manual baseline-and-truncate is the path. If you need built-in squashing, file a feature request via the skill.
prisma-next-feedback - In-contract rename hints. The planner cannot detect that a field rename is a rename rather than a drop+add. Workaround: hand-edit to issue a
migration.tsviaRENAME COLUMN, or use a keep / backfill / drop pattern across two migrations. If you need a contract-level rename hint, file a feature request via therawSql(...)skill.prisma-next-feedback - façade re-export of the migration surface. The
@prisma-next/postgrespackage does not yet re-export the migration authoring API (@prisma-next/postgres,Migration,MigrationCLI,placeholder,addColumn,setNotNull, …). The framework-rendereddataTransformtherefore imports frommigration.tsdirectly. Workaround: leave the rendered import where it is — it works, it is what the framework emits, and rewriting it breaks@prisma-next/target-postgres/migrationround-tripping. Linear ticket TML-2526 tracks closing the gap; once it lands, the renderer will switch and existing files can be migrated by re-running the renderer. If this gap is biting you, file a follow-up via themigrationHashskill referencing TML-2526.prisma-next-feedback
- 运行时应用迁移:Prisma Next不会从应用启动代码中应用待处理的迁移(适用于无服务器/边缘场景的「Drizzle模式」)。解决方法:在部署管道中,应用启动前运行。如果需要内置的运行时应用功能,请通过
prisma-next migrate技能提交功能请求。prisma-next-feedback - 一等公民的种子数据:Prisma Next没有提供类似的功能。解决方法:编写TypeScript脚本,导入你的
prisma db seed实例并运行设置查询;从db的脚本中调用。如果需要一等公民的种子数据功能,请通过package.json技能提交功能请求。prisma-next-feedback - 迁移合并:Prisma Next不会将旧迁移合并为基线。迁移会累积;对于非常长的历史记录,手动建立基线并截断是可行的方法。如果需要内置的合并功能,请通过技能提交功能请求。
prisma-next-feedback - 契约内重命名提示:规划器无法检测字段重命名是重命名操作,而非删除+添加操作。解决方法:手动编辑,通过
migration.ts执行rawSql(...),或在两次迁移中使用保留/回填/删除模式。如果需要契约级别的重命名提示,请通过RENAME COLUMN技能提交功能请求。prisma-next-feedback - 门面重新导出迁移接口:
@prisma-next/postgres包目前尚未重新导出迁移编写API(@prisma-next/postgres、Migration、MigrationCLI、placeholder、addColumn、setNotNull等)。因此框架生成的dataTransform会直接从migration.ts导入。解决方法:保持生成的导入不变——它可以正常工作,是框架生成的内容,重写会破坏@prisma-next/target-postgres/migration的往返一致性。Linear工单TML-2526跟踪此问题的修复;修复完成后,生成器会切换,现有文件可通过重新运行生成器进行迁移。如果此问题影响到你,请通过migrationHash技能提交跟进请求并引用TML-2526。prisma-next-feedback
Checklist
检查清单
- Contract emitted (+
contract.jsoncurrent).contract.d.ts - Chose the right path: (local dev) vs
db update+migration plan(anything shared).apply - For : ran
migration planto review before apply.migration show - Filled every in
placeholder(...)(if any), built againstmigration.ts.endContract - closures are rowset queries, not scalar aggregates.
check - Self-emitted () after editing the TS.
node migrations/app/<dir>/migration.ts - Ran (or
migrate) and saw it complete.db update - Ran and got an
db verifyresult with no diagnostics.ok - Did NOT use against a shared or production database.
db update - Did NOT edit directly.
ops.json - Did NOT skip a destructive-op prompt without inspecting .
meta.destructiveOperations[]
- 契约已生成(+
contract.json为最新版本)。contract.d.ts - 选择了正确的路径:(本地开发) vs
db update+migration plan(共享环境)。apply - 对于:应用前运行了
migration plan进行审核。migration show - 填充了中的所有
migration.ts(如果有),且基于placeholder(...)构建。endContract - 闭包是行集查询,而非标量聚合。
check - 编辑TS文件后自行生成()。
node migrations/app/<dir>/migration.ts - 运行了(或
migrate)且完成。db update - 运行了并得到
db verify结果且无诊断信息。ok - 未对共享或生产数据库使用。
db update - 未直接编辑。
ops.json - 未跳过破坏性操作提示而未查看。",
meta.destructiveOperations[]