关于 prisma-next-migrations
prisma-next-migrations 是 Prisma Next 的迁移编写技能,面向需要将数据契约变更应用到数据库的开发者。它涵盖快速更新与可审查迁移两种路径,以及数据转换的编写和故障恢复。
主要功能
- 01规划并应用数据库迁移
- 02选择快速更新或正式迁移
- 03填写数据转换占位符
- 04重新生成迁移清单与哈希
- 05排查迁移错误与数据库漂移
适用场景
- 01在本地开发库快速应用契约变更
- 02为共享分支编写可审查迁移
- 03为新增字段编写数据回填
- 04修复迁移哈希不匹配或部分应用
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ prisma/ prisma-next --skill prisma-next-migrations安装后在 Claude Code 中输入/prisma-next-migrations确认已加载。
详细介绍
Prisma Next — 迁移编写
编辑数据契约。Prisma Next 规划迁移。你来填写任何数据转换。
三步用户模型:
- 你编辑数据契约。(
prisma-next-contract) - Prisma Next 为你规划迁移。 ← 此技能
- 如果需要数据转换,你编辑
migration.ts并自行发出迁移。 ← 此技能
契约变更后,你可以选择如何将变更应用到数据库。此技能涵盖两种路径(db update 和 migration plan + migrate)、迁移包契约、migration.ts 编写 API,以及无需跳出工作流即可恢复的故障情况。
目标数据库。 迁移编写功能优先支持 Postgres 和 Mongo。CLI 从 prisma-next.config.ts 读取目标数据库(在 prisma-next init --target … 期间设置)。迁移命令不接受 --target 标志——请使用针对所需目标数据库配置的配置文件。以下示例会在目标数据库之间存在差异时,指出特定目标的导入、标记、工厂和事务行为。
适用场景
- 用户编辑了契约,并希望将变更应用到数据库。
- 用户希望编写带有数据转换的迁移。
- 用户希望对本地数据库运行待处理的迁移。
- 用户遇到
MIGRATION.HASH_MISMATCH、PN-MIG-2001(未填写占位符)或部分应用的迁移。 - 用户提及:migrate、migration、db push、db update、
prisma migrate dev、prisma migrate deploy、漂移、哈希不匹配、数据回填。
不适用场景
- 用户想了解部署 / 合并时将运行哪些迁移,或管理引用和不变量 →
prisma-next-migration-review。 - 用户想编辑契约 →
prisma-next-contract。 - 用户想深入了解单个结构化错误信封 →
prisma-next-debug。
关键概念
- **
db update(快捷路径)。**读取生成的契约,与实时数据库进行差异比较并应用变更。可选的--dry-run会打印计划,但不执行。破坏性操作需要交互式确认(或使用-y自动接受)。**不会写入迁移目录。**此路径不处理需要数据转换的操作——db update会完全排除data操作类别,并在需要数据转换时提前终止。仅可用于没有与其他人共享历史记录的数据库(你的本地开发数据库)。 - **
migration plan(正式路径)。**读取生成的契约,与磁盘迁移图的头节点进行差异比较,并在migrations/app/<YYYYMMDDTHHMM>_<snake_slug>/下写入新的迁移包。如果有任何操作需要数据转换,迁移包的migration.ts中会包含需要你填写的placeholder(...)调用。 - **迁移路径中的
app/段是使用该契约的应用程序的契约空间 ID。**你编写的所有迁移都存放在migrations/app/下。契约依赖的扩展有各自的同级目录(migrations/<extension-space-id>/)——这些目录由扩展包管理,你无需写入其中。首次针对应用级配置运行migration plan/db init时,会自动生成app/段。 - 迁移包文件(位于每个
migrations/app/<dir>/内):migration.json— 清单(元数据 +migrationHash)。ops.json— 规范操作列表。采用内容寻址;migrationHash根据此文件计算。migration.ts— TypeScript 编写源码,由migration plan(或migration new)通过框架渲染。你需要编辑其中的特定空位(见下文 填写占位符),然后运行该文件重新生成ops.json/migration.json。
- 契约快照。
migration.ts从共享的内容寻址存储中导入其起始与结束契约,文件位于migrations/snapshots/<hex>/contract.json+contract.d.ts(<hex>是契约的 64 位十六进制存储哈希)——而不是从迁移包内的文件导入。 - **自行生成。**运行
node migrations/app/<dir>/migration.ts,即可从(可能已编辑的)TS 源码重新生成ops.json和migration.json。编辑后,只有此方式受支持,可用于更新现有迁移包。 - **
migration.ts的结构。**由框架渲染。它是一个继承Migration的类(Mongo 使用@prisma-next/family-mongo/migration,Postgres 使用通过@prisma-next/postgres/migration重新导出的版本——见下方的框架代码块),并带有返回工厂调用值数组的operationsgetter。文件末尾是MigrationCLI.run(import.meta.url, M),因此执行该文件会自行生成迁移文件。 - **
placeholder(slot)。**规划器会在需要数据转换的地方,将此哨兵值写入渲染后的migration.ts(Mongo 使用@prisma-next/errors/migration,Postgres 使用@prisma-next/postgres/migration导入)。在生成时调用placeholder(...)会抛出PN-MIG-2001未填写迁移占位符。用户需要将() => placeholder(...)箭头函数替换为真实的查询计划闭包(Postgres),或填写dataTransform({ check, run })源码(Mongo——见 填写占位符),然后自行生成迁移文件。 - **
this.dataTransform(endContract, name, { check, run })。**数据转换工厂。check是一个行集查询;只要有任意行,就表示“仍有工作待处理”。run是一个或多个执行回填的变更查询。二者都是惰性闭包,会返回基于endContract构建的查询计划。运行器会将check包装为预检查用的EXISTS(...)和后检查用的NOT EXISTS(...),因此同一个闭包可以同时断言“仍有工作”和“工作已完成”。 pendingPlaceholders。migration plan的 JSON 结果中的布尔字段。值为true表示迁移包已写入,但包含未填写的占位符——在你编辑migration.ts并自行生成迁移文件之前,migrate会抛出PN-MIG-2001。- **
migrationHash。**迁移包的内容寻址标识。如果migration.json中存储的哈希与根据磁盘文件重新计算出的哈希不一致,就会触发MIGRATION.HASH_MISMATCH(几乎总是因为有人编辑了migration.ts却未自行生成迁移文件)。 - **标记。**记录“此数据库在空间 Y 中处于契约哈希 X”。Postgres:
prisma_contract.marker中的一行。Mongo:_prisma_migrations集合中的一个文档(以空间为键)。每次成功的迁移都会在该空间通过架构验证后,将标记前移一次。db sign会根据当前契约哈希写入标记,但只有在架构验证通过后才会执行(如果数据库实时架构与契约不一致,它不会为数据库签名)。 - **应用原子性。****Postgres:**每次迁移都在
BEGIN ... COMMIT内运行;如果失败,Postgres 会回滚,标记则保持在上一次迁移的to哈希。**Mongo:**DDL 操作(createCollection、createIndex、collMod、setValidation等)不会放在多文档事务中;运行器会应用操作,根据目标契约验证实时架构,并仅在验证通过后推进标记(可跨空间恢复——见 MongoDB 系列文档)。普通 DDL +dataTransform工作流会保持一致;迁移中途失败留下的部分状态会通过db verify/db schema诊断,不会被忽略。 - **操作类别。**每个操作都会声明一个
operationClass:additive、widening、data或destructive。CLI 会在计划预览和 JSON 输出中显示这些类别。没有long-running类别,框架也不会生成CREATE INDEX CONCURRENTLY——操作始终处于事务中。
migration.ts 由框架渲染,而非手工编写
migrations/<space-id>/<timestamp>/migration.ts 下的文件(对于你自己的应用,<space-id> 始终为 app/)由框架为你渲染——每当契约发生变化,prisma-next migration plan 都会写入一个已填充的包;当你想直接编写操作时,prisma-next migration new 会写入一个空白脚手架。不要从头编写这些文件。你需要编辑框架留下的特定空缺——主要是替换 placeholder("<slot>") 哨兵(Postgres),或填充 dataTransform({ check, run }) 管道槽位(Mongo)——然后自行生成。
Postgres 渲染的导入指向 @prisma-next/postgres/migration(SQLite 项目则指向 @prisma-next/sqlite/migration)。
Mongo 渲染的导入使用 @prisma-next/family-mongo/migration 中的 Migration 基类,以及 @prisma-next/target-mongo/migration 中的操作工厂(createIndex、dataTransform 等)。MigrationCLI 来自 @prisma-next/cli/migration-cli。
在两个目标平台上,都应将渲染生成的导入行视为由框架管理:
- 保留原样,不要将它们改写为其他
@prisma-next/<…>路径;框架渲染器生成的形式是权威形式,你手动做出的任何更改都会在下次重新渲染或自行生成软件包时被还原(还可能触发MIGRATION.HASH_MISMATCH)。 - 如果需要额外的工厂符号,将其添加到现有的渲染导入行中(Postgres:
@prisma-next/postgres/migration;Mongo:@prisma-next/target-mongo/migration),而不是从其他@prisma-next/...子路径引入第二条导入。 - “用户代码仅从
@prisma-next/<target>导入”这一约定适用于你自己的模块(查询、运行时设置、契约编写)。框架渲染的migration.ts脚手架属于框架的接口,而非你的;此规则对这个文件不适用。
你需要处理的诊断代码
| 代码 | 来源 | 处理方式 |
|---|---|---|
PN-MIG-2001 未填充迁移占位符 | 在生成时抛出 placeholder(...) | 打开 migration.ts,将指定的 placeholder("<slot>") 调用替换为实际的查询闭包,然后自行生成。 |
PN-MIG-2002 未找到 migration.ts | 读取迁移包 | 该包格式错误。通过版本控制恢复,或运行 prisma-next migration new 创建一个新的迁移包。 |
PN-MIG-2003 无效的默认导出 | 加载 migration.ts | 文件的默认导出不是 Migration 子类或工厂函数。通过版本控制恢复规划器生成的脚手架,或重新运行 migration plan 以生成干净的迁移包。 |
PN-MIG-2005 dataTransform 契约不匹配 | 构建数据转换查询计划 | 查询构建器实例化时使用的契约引用,与传递给 this.dataTransform(...) 的 endContract 不同。两者都使用在模块作用域导入的 endContract。 |
MIGRATION.HASH_MISMATCH 迁移包已损坏 | migrate(或任何读取迁移包的操作) | 编辑了 ops.json / migration.json,但未自行生成。运行 node migrations/app/<dir>/migration.ts 重新生成,然后重新运行 migrate。 |
PN-RUN-3002 哈希不匹配 | db verify | 标记与契约哈希不一致(Postgres: prisma_contract.marker;Mongo: _prisma_migrations)。数据库中的契约版本与代码认为的版本不同。可以向前运行迁移;或者,如果数据库状态正确,而标记在手动修正后已过时,则运行 db sign。 |
PN-RUN-3001 数据库尚未签名 | 任何需要标记的命令 | 数据库尚无标记。运行 prisma-next db init --db <url> 为一个空数据库设置基线,或运行 db update --db <url> 直接应用当前契约。 |
决策 — 选择哪条路径?
| 情况 | 路径 | 原因 |
|---|---|---|
| 本地开发,模式仍在变化 | db update | 快速、交互式,无需迁移文件。 |
| 与其他开发者共用分支 | migration plan + migrate | 可重放、可审查,并经过内容哈希。 |
| 任何要进入生产环境的变更 | migration plan + migrate | 生产环境必须运行经过审查且已哈希的迁移。 |
| 添加需要回填数据的列 | migration plan(写入 placeholder),编辑 migration.ts,自行生成,再运行 migrate | db update 不会编写数据转换;正式路径可以。 |
| 从漂移中恢复(数据库与契约不一致) | 手动修复后运行 db sign,或在 PN 能规划修复方案时运行 migration plan | 取决于哪一方正确。见下文 从漂移中恢复。 |
开发 → 发布过渡(db ref 模式)
示例 — 在本地使用 db update 迭代,然后发布首个正式迁移:
pnpm prisma-next db init --db $DATABASE_URL
pnpm prisma-next contract emit && pnpm prisma-next db update --db $DATABASE_URL
pnpm prisma-next contract emit && pnpm prisma-next migration plan --name add_feature
pnpm prisma-next migrate --db $DATABASE_URL
pnpm prisma-next db verify --db $DATABASE_URL
db ref 是 migrations/app/refs/db.json 中的一个具名指针 — 内容只有 { hash, invariants }。它记录项目的开发数据库已更新到哪个契约哈希 — 这是离线规划器在规划时无需建立连接即可判断“我的本地数据库处于什么状态”的替代方式。它所指向的契约通过 migrations/snapshots/<hex>/contract.json 中共享的内容寻址存储解析,所有迁移图节点都通过相同的存储解析。
db init / db update 的写入内容。 在项目的默认 --db URL 上运行时(未显式指定 --db 标志),这两个命令都会隐式推进 db ref:先将命令执行后的契约 IR(如果不存在)写入快照存储,然后写入 ref 指针。使用 --advance-ref <name> 可覆盖 ref 名称。传入 --db <non-default-url> 时,除非显式指定 --advance-ref,否则不会推进 ref — 协调另一个数据库不等同于记录项目开发环境的检查点。
磁盘上的布局只是这个指针:
migrations/app/refs/
└── db.json # { "hash": "<hex>", "invariants": [] }
开发迭代后的首次 migration plan。 migration plan 默认将 --from 设为 db ref。当磁盘上的迁移图仍然为空,且 db ref 指向非空哈希并且存储中存在对应条目时(通常是在执行一次或多次 db update 后),规划器会生成两个包,而不是一个:
- 基线:
null → from-hash(将from-hash引入为图节点) - 差异:
from-hash → current_contract
一次调用就会将两者都写入磁盘 — 预期 git status 中会出现两个新目录。随后 migrate 会找到经过基线的路径并应用差异。这解决了开发 → 发布过程中因单包计划引用的哈希尚未成为图节点,而产生无法应用的迁移(应用时出现 MIGRATION.PATH_UNREACHABLE)的问题。
忘记传标志的陷阱。 图变为非空后,默认的 db ref 可能指向超出图末端的位置(你在迭代时每次运行 db update 都推进了 ref,但从未提交迁移)。下一次使用隐式默认值的 migration plan 会因 MIGRATION.HASH_NOT_IN_GRAPH 而拒绝执行,并列出指向图节点的可达 refs。
规划时遇到 MIGRATION.HASH_NOT_IN_GRAPH 时的恢复方法:
# Option A — plan from a graph node explicitly
pnpm prisma-next migration plan --from production --name my_change
# Option B — realign the db ref to a graph-node hash, then plan with the default
pnpm prisma-next ref set db <graph-node-hash>
pnpm prisma-next migration plan --name my_change
如果 db ref 的指针本身不存在,并且该哈希也不是图节点(MIGRATION.SNAPSHOT_MISSING),请使用 ref set db <hash> 创建它,或使用 db update --advance-ref db 推进它。
直接运行 migrate 后。 migrate 不会隐式推进 db ref(面向生产环境的命令会保持显式操作)。活动标记会推进,而 ref 可能会滞后。可通过运行 db update(数据库已是最新状态时不会对数据库执行操作)或在同一次调用中运行 migrate --advance-ref db 来刷新。
何时切换路径。 如果是在个人开发数据库上迭代中的架构,使用 db update。如果变更需要可供审查、可重复执行的迁移(通常是在创建 PR 或操作任何共享环境之前),则切换到 migration plan + migrate。db 引用连接了这两条路径:它会将开发迭代状态保存到磁盘,这样首次正式规划就能知道你上次进行到了哪里。
图节点规则(规划时)。 任何用作 from 端的哈希——显式指定的 --from、默认的 db 引用或引用名称——在图非空时都必须已经是磁盘上迁移图中的节点。自动基线的双包生成是唯一例外:它仅适用于空图,且必须满足 from 通过引用解析后非空,并且有可用的存储条目。如果引用的指针缺失,且该哈希也不是图节点,规划会拒绝执行,并返回 MIGRATION.SNAPSHOT_MISSING。
应用时的补充检查。 migrate 会在执行 DDL 前读取实时标记。如果标记哈希不是图节点,命令会拒绝执行,并返回 MIGRATION.MARKER_MISMATCH,从而捕获离线规划器无法发现的漂移。这与 MIGRATION.MARKER_NOT_IN_HISTORY 不同;后者会在运行器遍历图时稍后触发,表示标记不在当前遍历路径上。完整诊断目录请参阅 prisma-next-migration-review。
db 是默认引用名称,而非保留名称。框架会在下一个开发周期覆盖它;你可以显式执行 ref set db <hash>,但请注意,之后针对默认 URL 运行 db update 时会替换该引用。
权威详情:Migration System § Contract resolution through the snapshot store、§ migration plan、§ Recovery affordances、ADR 218 — Refs with paired contract snapshots and universal graph-node invariant(TML-2629,其配对快照部分已被取代——请参阅 ADR 的 Status 注释),以及 ADR 240 — Contract snapshots live in a content-addressed store。
工作流 — db update(快速路径)
概念:db update 会针对实时数据库解析目标(emitted contract),并应用差异。使用 --dry-run 预览。除非传入 -y 或 --no-interactive,否则破坏性操作会提示用户进行交互确认。这条路径完全不包含 data 类操作——如果差异需要数据转换,db update 会以规划错误失败,此时应切换到 migration plan 来编写转换。
编辑合约后运行:
pnpm prisma-next contract emit
# Postgres: --db postgresql://...
# Mongo: --db mongodb://... (dev scaffolds often need ?replicaSet=rs0)
pnpm prisma-next db update --db $DATABASE_URL --dry-run
pnpm prisma-next db update --db $DATABASE_URL
db update 会在成功时验证架构并推进标记,因此在正常情况下,后续再运行 db verify 是多余的。只有在需要单独诊断时才使用 db verify(参见 验证合约与数据库)。
检查 JSON 输出,以决定下一步操作:
pnpm prisma-next db update --db $DATABASE_URL --json
JSON 包含 plan.operations[],其中列出每项操作的 operationClass;在应用模式下,还包含 execution.operationsExecuted 和应用后的 marker.storageHash。如果命令因破坏性操作而失败,错误信封中的 meta.destructiveOperations[] 会准确列出将被删除的内容。
工作流 — migration plan + migrate(正式流程)
概念:migration plan 会在磁盘上写入一个新的迁移包。如果规划器需要进行任何数据转换,该迁移包就会处于待处理状态——migration.ts 中会保留 placeholder(...) 调用,直到你补全它们。migrate 会按图中的顺序,以事务方式运行所有待处理的迁移包。
规划一项变更:
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— 与具体类型无关的文本预览。
检查迁移包:
pnpm prisma-next migration show
pnpm prisma-next migration show <dirName-or-migrationHash-prefix>
migration show 会显示单个迁移包。要查看将要运行的迁移列表(涵盖所有契约空间),请使用 migrate --show:
# Online: reads the live DB marker as the origin.
pnpm prisma-next migrate --show --db $DATABASE_URL
# Offline: hypothetical path from any ref or hash.
pnpm prisma-next migrate --show --from <hash-or-ref> --to <hash-or-ref>
migrate --show 为只读操作,不会写入数据库或迁移图。应用迁移前请使用它确认执行顺序。
补全所有数据转换(参见补全占位符),如果编辑了 migration.ts,则自行生成,然后:
pnpm prisma-next migrate --db $DATABASE_URL
migrate 运行时不会提示确认——破坏性操作的确认步骤在 db update 中,而不在此处。应用前,请检查计划输出或 migration show 中的破坏性操作。
工作流 — 补全占位符
概念:规划器可以检测到需要进行数据转换,却无法确定该转换应该如何执行。它会写入带类型的脚手架代码并停止;你需要补全转换,然后自行生成。
Postgres
规划器可以检测到需要进行数据转换(例如,为没有默认值的新 NOT NULL 列回填数据),却无法确定该转换应该如何执行。你需要使用基于 endContract 构建的实际查询计划,补全 check 和 run 闭包。
规划器生成的脚手架代码如下:
// migrations/app/20260515T1200_add_user_name/migration.ts
import endContract from '../../snapshots/93f07d1b…c9e1e5a2/contract.json' with { type: 'json' };
import { Migration, MigrationCLI, addColumn, placeholder } from '@prisma-next/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(...) 调用都替换为基于 endContract 构建的查询计划闭包。check 闭包必须返回一个只要存在任意行就表示“仍有工作待完成”的行集查询——通常为 <table>.select('id').where(<violation predicate>).limit(1)。标量或聚合类型(count(*)、bool_and(...))会导致契约被悄然破坏:运行器会将 check 包装两次(预检查使用 EXISTS(...),后检查使用 NOT EXISTS(...)),而始终返回一行的查询会使 EXISTS 始终为真、NOT EXISTS 始终为假。
请基于 endContract 构建查询构造器,以确保存储哈希一致——使用不同的契约引用会引发 PN-MIG-2005。补全后的结构(即上方渲染的脚手架代码中将 placeholder(...) 调用替换后的代码;如果需要额外的工厂函数,例如 setNotNull,请将其添加到现有的 @prisma-next/postgres/migration 导入行中,而不是另写一条导入语句)。关于周边的 db 设置,参见 prisma-next-queries:
import endContract from '../../snapshots/93f07d1b…c9e1e5a2/contract.json' with { type: 'json' };
import { Migration, MigrationCLI, addColumn, setNotNull } from '@prisma-next/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);
自行生成:
node migrations/app/20260515T1200_add_user_name/migration.ts
自行生成会重新生成 ops.json,并重新计算 migration.json 中的 migrationHash。下一次运行 migrate 时,迁移包就会保持一致。
Mongo
Mongo 的 dataTransform 操作接受 { check, run } 对象,其 source / run 会返回 Mongo 查询计划类型(通常为来自 @prisma-next/mongo-query-ast/execution 的 RawAggregateCommand / RawUpdateManyCommand)。规划器可能会在这些源中留下 placeholder(...),直到你补全它们。每个渲染后的 migration.ts 都包含 describe() 首尾标记(from / to 契约哈希)——为简洁起见,上面的 Postgres 示例省略了这些标记。从 @prisma-next/target-mongo/migration 导入工厂函数:
import { MigrationCLI } from '@prisma-next/cli/migration-cli';
import { Migration } from '@prisma-next/family-mongo/migration';
import { createIndex, dataTransform } from '@prisma-next/target-mongo/migration';
import { RawAggregateCommand, RawUpdateManyCommand } from '@prisma-next/mongo-query-ast/execution';
class M extends Migration {
override describe() {
return { from: '<hex>', to: '<hex>', labels: ['normalize-names'] };
}
override get operations() {
return [
createIndex('users', [{ field: 'name', direction: 1 }]),
dataTransform('lowercase-user-name', {
check: {
source: () => ({
collection: 'users',
command: new RawAggregateCommand('users', [
{ $match: { name: { $regex: '[A-Z]' } } },
{ $limit: 1 },
]),
meta: { target: 'mongo', storageHash: '…', lane: 'mongo-pipeline', paramDescriptors: [] },
}),
},
run: () => ({
collection: 'users',
command: new RawUpdateManyCommand(
'users',
{ name: { $exists: true } },
[{ $set: { name: { $toLower: '$name' } } }],
),
meta: { target: 'mongo', storageHash: '…', lane: 'mongo-raw', paramDescriptors: [] },
}),
}),
];
}
}
export default M;
MigrationCLI.run(import.meta.url, M);
以相同方式自行生成:node migrations/app/<dir>/migration.ts。
工作流 — 手动编写迁移
概念:当规划器没有可规划的内容时(例如自定义数据修复、安装扩展或建立基线),相同的 Migration 类结构也能让你直接编写操作。即便如此,你也无需从头编写文件——migration new 会为你生成一个空包,你只需编辑其中的 operations getter,然后自行生成。
pnpm prisma-next migration new --name <snake_slug>
将目标对应的工厂名称添加到框架生成的导入行中(Postgres:@prisma-next/postgres/migration;Mongo:@prisma-next/target-mongo/migration)。使用 --help 查看帮助,并查看渲染器生成的导入列表。
Postgres 工厂(代表性集合):
- 表:
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 })(实例方法,不是独立工厂)。
Mongo 工厂(来自 @prisma-next/target-mongo/migration):
- 集合:
createCollection、dropCollection、validatedCollection、setValidation。 - 索引:
createIndex、dropIndex。 - 集合选项:
collMod。 - 数据转换:
dataTransform(name, { check, run })(独立工厂;check/run使用 Mongo 查询计划结构)。
每次编辑后自行生成(node migrations/app/<dir>/migration.ts)。
工作流 — 检查实时架构
概念:db schema 是只读的,绝不会写入文件。默认情况下,它会以树形结构打印实时架构,也可以使用 --json 输出 JSON。在规划期间和验证过程中都可以使用它。
pnpm prisma-next db schema --db $DATABASE_URL
pnpm prisma-next db schema --db $DATABASE_URL --json > schema.json
没有内置的筛选标志——如果只想查看一张表,可以将 JSON 传递给 jq(或你喜欢的 JSON 工具)进行处理。
工作流 — 验证契约与数据库(诊断)
概念:db verify 是独立的诊断命令——在顺利执行的情况下,它不是 db update 或 migrate 之后的常规步骤(这些命令成功后已经会进行验证并更新标记)。如果怀疑存在漂移,或需要证明数据库与契约匹配,请使用 db verify:
- 在 Prisma Next 之外执行手动 SQL 或临时编辑后。
- 从备份恢复数据库时。
migrate失败或部分应用时(尤其是在 Mongo 上,DDL 可恢复执行,而不是通过事务包装)。- 运行时或其他命令出现
PN-RUN-3002/PN-RUN-3001时。
模式:
- 默认——完整验证(架构 + 标记)。
--marker-only——跳过架构验证,只检查标记。--schema-only——跳过标记验证,只检查架构是否符合契约。--strict会额外检查:契约中不存在的架构元素将被视为错误(默认允许“数据库包含额外元素”)。
pnpm prisma-next db verify --db $DATABASE_URL
不匹配时,错误封套会指出失败模式(PN-RUN-3002 哈希不匹配、PN-RUN-3001 标记缺失、目标不匹配,以及带结构化路径的架构问题)。
工作流 — 重新签署标记
概念:db sign 会将标记重写为当前契约哈希。当数据库是事实来源而标记已过期时,可在手动修复后使用。db sign 会先进行架构验证,如果数据库架构与契约不一致,就会拒绝签署——因此,签署成功始终意味着架构匹配且标记现已正确。
pnpm prisma-next db sign --db $DATABASE_URL
工作流 — 从漂移中恢复
概念:漂移是指 db verify 报告实时 DB schema 与标记所声明的预期状态不匹配。可采取两种有效操作,具体取决于哪一方正确:
- 契约正确;DB 有误 → 运行迁移。可以使用
db update(快捷方式,仅限开发 DB),也可以使用migration plan+migrate(其他所有场景)。 - DB 正确;契约或标记有误 → 编辑契约,使其与 DB 匹配(参见
prisma-next-contract),生成产物,然后运行db sign刷新标记。
用于判断哪一方正确的诊断命令:
pnpm prisma-next db schema --db $DATABASE_URL --json
pnpm prisma-next db verify --db $DATABASE_URL --json
使用 db verify 确认哪一方有误,然后在执行任一分支后重新运行,直到返回 ok 且没有诊断信息。
工作流 — 从部分应用的迁移中恢复
概念:在 Postgres 上,每个迁移都在事务中应用——迁移中途失败会回滚,标记会保留在上一个迁移的 to 哈希。在 Mongo 上,DDL 可以恢复执行,并且标记只有通过验证后才会推进;使用 db verify / db schema 进行诊断,修复失败包中的 migration.ts,自行生成产物,然后重新运行 migrate。
可能泄漏部分状态的失败包括:事务包装器之外的 Postgres rawSql(...) 步骤、验证失败前已部分应用的 Mongo DDL,或外部副作用(从 run 闭包调用其他系统)。
诊断:
pnpm prisma-next db verify --db $DATABASE_URL --json
pnpm prisma-next db schema --db $DATABASE_URL --json
修复并重新运行 migrate:
node migrations/app/<dir>/migration.ts
pnpm prisma-next migrate --db $DATABASE_URL
如果失败是由导致外部系统处于半变更状态的带外副作用引起,请先手动修复这些系统,再重新应用。
工作流 — 解决 MIGRATION.HASH_MISMATCH
概念:migrationHash 基于内容寻址。哈希不匹配意味着 migration.json 中存储的哈希与根据 ops.json(及元数据)重新计算出的哈希不一致。原因几乎总是有人编辑了 migration.ts,却忘了自行生成产物。解决方法是自行生成出错包的产物。
node migrations/app/<dir>/migration.ts
pnpm prisma-next migrate --db $DATABASE_URL
如果自行生成产物本身失败(例如契约已更新,而这些操作不再适用于迁移的结束契约),说明该包已过时。可以从版本控制中恢复它,或删除它并使用 migration plan 重新规划。
工作流 — 处理破坏性操作提示(仅限 db update)
概念:当 db update 将删除列或表时,它会暂停并询问是否继续。此提示仅适用于 db update——migrate 不会提示,而是直接运行迁移包中包含的操作,因此请在运行 migrate 前检查计划或调用 migration show。
当 db update 在交互模式下报告破坏性操作时,警告会列出这些操作。提示内容为:
应用破坏性更改?此操作无法撤销。
处理方式:
- 如果不再需要这些数据,请回答是。
- 回答否,然后选择以下任一方式:
- 通过
migration plan重新调整迁移,并手动编辑migration.ts以保留数据(例如,先复制到新列,再删除原列);或 - 撤销契约更改,以跳过破坏性操作。
- 通过
在非交互场景中(CI、--no-interactive、--json),破坏性操作的响应会作为结构化错误返回——meta.destructiveOperations[] 会列出将被删除的内容。使用 -y 重新运行以自动接受,或逐项处理每个操作。
常见陷阱
- 对共享或生产数据库使用
db update。 绝不要这样做。此更改不会留下迁移历史。请使用migration plan+migrate。 - 跳过数据转换。 在
migration.ts中保留placeholder(...)会导致下一次migrate抛出PN-MIG-2001。请填完每个占位符,并自行生成。 - 直接编辑
ops.json。 它是规范产物,不是编写源文件。请编辑migration.ts,然后自行生成。 - 编辑
migration.ts后忘记自行生成。 下一次migrate要么使用过时的ops.json(如果你只添加了注释),要么失败并报错MIGRATION.HASH_MISMATCH(如果你更改了操作)。请务必自行生成。 - 成功执行
db update或migrate后例行运行db verify。 在正常流程中这是多余的——请仅在诊断漂移时使用db verify(手动编辑、恢复、migrate失败)。 - Postgres
this.dataTransform中的聚合check闭包。 返回count(*)或bool_and(...)会破坏预检/后检约定——两端都会解析为常量。请使用行集形状:select('id').where(<violation>).limit(1)。 - 在一次迁移中引用两个契约。 针对与传给
this.dataTransform(endContract, ...)不同的契约构建查询计划会触发PN-MIG-2005。请务必在模块作用域只导入一次endContract,并使用同一个引用。 - 重命名并期待规划器检测到(Postgres)。 Prisma Next 目前没有契约内重命名提示;规划器会生成破坏性删除 + 添加。请手动编辑
migration.ts,将破坏性操作改写为发出ALTER TABLE ... RENAME COLUMN ...的rawSql({ ... })(或使用两次迁移的保留 / 回填 / 删除模式),然后自行生成。请参阅prisma-next-contract§ 编辑字段 — 重命名。 - 从空文件手动编写
migration.ts,或改写渲染后的导入行。 迁移文件由框架渲染——请让prisma-next migration plan(或migration new)渲染包,然后只编辑框架为你留出的空缺。在 Postgres 中,不要改动渲染出的@prisma-next/postgres/migration(或@prisma-next/sqlite/migration)导入路径;在 Mongo 中,请使用渲染出的@prisma-next/family-mongo/migration+@prisma-next/target-mongo/migration。请将符号添加到现有的工厂导入行,而不是引入新的导入路径。
Prisma Next 目前还不能做什么
- 在运行时应用迁移。 Prisma Next 不会从应用的启动代码中应用待处理的迁移(无服务器 / 边缘场景中的“Drizzle 模式”)。解决方法:在应用启动前,从部署流水线运行
prisma-next migrate。如果你需要内置的运行时迁移功能,请通过prisma-next-feedback技能提交功能请求。 - 将种子数据作为一等功能。 Prisma Next 不提供等效于
prisma db seed的功能。解决方法:编写一个导入db实例并运行初始化查询的 TypeScript 脚本;从package.json的脚本中调用它。如果你需要一等种子数据功能,请通过prisma-next-feedback技能提交功能请求。 - 压缩迁移。 Prisma Next 不会将较早的迁移压缩为基线。迁移会不断累积;对于非常庞大的历史记录,手动创建基线并截断是可行的方案。如果你需要内置压缩功能,请通过
prisma-next-feedback技能提交功能请求。 - 契约内重命名提示。 规划器无法检测字段重命名,而不是删除 + 添加。解决方法:手动编辑
migration.ts,通过rawSql(...)执行RENAME COLUMN,或在两次迁移中使用保留 / 回填 / 删除模式。如果你需要契约级重命名提示,请通过prisma-next-feedback技能提交功能请求。
图和历史命令
规划或应用后,你可以离线检查迁移图:
pnpm prisma-next migration list— 列出所有磁盘上的迁移,并以图树形式呈现。支持--legend(打印图形符号说明)、--ascii(适用于管道的图形符号)和--json。pnpm prisma-next migration log --db $DATABASE_URL— 从实时数据库读取已应用迁移的按时间顺序排列的平面表格。支持--ascii和--json。
查看完整的图拓扑:pnpm prisma-next migration graph(也支持 --legend、--ascii、--dot 和 --json)。
@@control 和 DDL 范围
@@control 策略将对象排除在 Prisma Next 管理范围之外时,这些对象不会出现在规划的 DDL 中。四种策略分别是:managed(Prisma 规划并应用 DDL)、tolerated(对象可以存在,不生成 DDL)、external(预期对象已存在,不生成 DDL)、observed(Prisma 读取但从不写入)。在架构中声明 @@control(managed|tolerated|external|observed);编写语法请参见 prisma-next-contract 和 packages/2-sql/2-authoring/contract-psl/README.md。
遥测
CLI 默认会收集匿名使用数据。若要选择退出,请在环境中设置 PRISMA_NEXT_DISABLE_TELEMETRY=1 或 DO_NOT_TRACK=1。完整的选择退出说明请参见 docs/Telemetry.md。
检查清单
- 已生成契约(
contract.json和contract.d.ts均为最新版本)。 - 选择了正确的路径:
db update(本地开发)或migration plan+migrate(任何共享环境)。 - 对于
migration plan:已运行migration show,并在运行migrate前检查。 - 已在
migration.ts中填写所有placeholder(...)(如有),并针对endContract构建。 -
check闭包是行集查询,而不是标量聚合。 - 编辑 TS 后已自行生成(
node migrations/app/<dir>/migration.ts)。 - 已运行
migrate(或db update),并确认其完成。 - 仅在诊断偏移时使用
db verify,不要将其作为常规的应用后步骤。 - 未对共享数据库或生产数据库使用
db update。 - 未直接编辑
ops.json。 - 检查
meta.destructiveOperations[]前,未跳过破坏性操作提示。