database-migrations
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDatabase Migrations with @schemavaults/dbh
使用 @schemavaults/dbh 进行数据库迁移
@schemavaults/dbhdbhup()down()Invoke the CLI with your package runner. Use for
validating and building migrations — uses Bun's
bundler and requires Bun anyway. Use for running /
applying migrations ( and ): most PostgreSQL drivers are
built for Node.js rather than Bun, so apply migrations on the Node runtime.
bunx @schemavaults/dbhbuild-db-migrationsnpx @schemavaults/dbhmigratereverse@schemavaults/dbhdbhup()down()使用你的包运行器调用 CLI。使用 来验证和构建迁移 —— 命令使用 Bun 的打包器,因此本身就需要 Bun 环境。使用 来运行/应用迁移( 和 ):大多数 PostgreSQL 驱动是为 Node.js 而非 Bun 构建的,因此请在 Node 运行时上执行迁移。
bunx @schemavaults/dbhbuild-db-migrationsnpx @schemavaults/dbhmigratereverseOne-time setup (for consumers)
一次性设置(面向使用者)
Migrations import the template tag from rather than directly from
the package. This indirection is required by the build step (see the note under
"Building migrations"), so configure it once:
sql@/sql-
Create a localmodule somewhere in your source tree, e.g.
sql, that re-exports the tag from the package:./src/db/sql.tsts// src/db/sql.ts export { sql, sql as default } from "@schemavaults/dbh/sql"; export type * from "@schemavaults/dbh/sql"; -
Configure thepath alias in your
@/sqlso migration sources typecheck and resolve:tsconfig.jsonjsonc{ "compilerOptions": { "baseUrl": ".", "paths": { "@/sql": ["./src/db/sql.ts"] } } } -
Create a migrations directory, e.g., and add your numbered migration files there.
./src/db/migrations/
迁移文件从 而非直接从包中导入 模板标签。这种间接层是构建步骤所必需的(参见“构建迁移”下的说明),因此只需配置一次:
@/sqlsql-
创建本地模块:在你的源码目录中创建一个文件,例如
sql,从包中重新导出该标签:./src/db/sql.tsts// src/db/sql.ts export { sql, sql as default } from "@schemavaults/dbh/sql"; export type * from "@schemavaults/dbh/sql"; -
在中配置
tsconfig.json路径别名,以便迁移源码可以通过类型检查和路径解析:@/sqljsonc{ "compilerOptions": { "baseUrl": ".", "paths": { "@/sql": ["./src/db/sql.ts"] } } } -
创建迁移目录,例如,并在其中添加带编号的迁移文件。
./src/db/migrations/
Migration file format
迁移文件格式
Each migration is a single file in your migrations directory. The rules are:
- The directory is non-empty.
- Each file name is prefixed with a 5-digit migration number, followed by a
short kebab-case description, e.g. ,
00000-template-migration.ts. The number defines apply order.00001-create-users-table.ts - Each module exports an and a
up(db)function.down(db)applies the change;up()must reverse it exactly so migrations can be rolled back.down() - Migration numbers are unique — never reuse a number. If two branches both
add , that collision must be resolved by renumbering one of them before merge.
00040-*.ts
Both and receive a instance and return a .
Import the type from the package: .
updownKysely<any>PromiseKyselyimport type { Kysely } from "@schemavaults/dbh"每个迁移都是迁移目录中的一个单独文件。规则如下:
- 目录非空。
- 每个文件名以5位迁移编号为前缀,后跟简短的 kebab-case 格式描述,例如 、
00000-template-migration.ts。编号决定了应用顺序。00001-create-users-table.ts - 每个模块导出 和
up(db)函数。down(db)应用变更;up()必须精确回滚该变更,以便迁移可以撤销。down() - 迁移编号唯一 —— 绝不要重复使用编号。如果两个分支都添加了 ,必须在合并前通过重新编号其中一个来解决冲突。
00040-*.ts
updownKysely<any>PromiseKyselyimport type { Kysely } from "@schemavaults/dbh"Example: using the Kysely<any>
query builder
Kysely<any>示例:使用 Kysely<any>
查询构建器
Kysely<any>Prefer the typed query builder for schema operations:
ts
// 00001-create-users-table.ts
import type { Kysely } from "@schemavaults/dbh";
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable("users")
.addColumn("user_id", "uuid", (col) => col.primaryKey())
.addColumn("email", "text", (col) => col.notNull().unique())
.addColumn("created_at", "bigint", (col) => col.notNull())
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema.dropTable("users").execute();
}优先使用类型化查询构建器进行 schema 操作:
ts
// 00001-create-users-table.ts
import type { Kysely } from "@schemavaults/dbh";
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable("users")
.addColumn("user_id", "uuid", (col) => col.primaryKey())
.addColumn("email", "text", (col) => col.notNull().unique())
.addColumn("created_at", "bigint", (col) => col.notNull())
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema.dropTable("users").execute();
}Example: using the sql
template tag
sql示例:使用 sql
模板标签
sqlFor statements the builder can't express (or raw DDL), import from
(your local module from setup, which re-exports Kysely's tag) and
call :
sql@/sqlsql.execute(db)ts
// 00002-create-squirrels-table.ts
import type { Kysely } from "@schemavaults/dbh";
import { sql } from "@/sql";
export async function up(db: Kysely<any>): Promise<void> {
await sql`
CREATE TABLE IF NOT EXISTS EXAMPLE_SQUIRRELS (
squirrel_id UUID PRIMARY KEY,
squirrel_name TEXT NOT NULL,
created_at BIGINT NOT NULL
);
`.execute(db);
// Always interpolate values via ${...}; the sql tag parameterizes them.
await sql`CREATE INDEX squirrels_name_idx ON EXAMPLE_SQUIRRELS (squirrel_name);`.execute(
db,
);
}
export async function down(db: Kysely<any>): Promise<void> {
await sql`DROP TABLE IF EXISTS EXAMPLE_SQUIRRELS;`.execute(db);
}Important: migration files must always importfromsql, never directly from@/sql. The@schemavaults/dbh/sqlstep rewrites the literalbuild-db-migrationsimport specifier to a relative path pointing at the built, standalone@/sql, so the import must be written exactly assql.jsfor the build to work. (This is why the one-time setup configures the@/sqlalias.)@/sql
对于构建器无法表达的语句(或原生 DDL),从 导入 (即你在设置中创建的本地模块,它重新导出 Kysely 的 标签)并调用 :
@/sqlsqlsql.execute(db)ts
// 00002-create-squirrels-table.ts
import type { Kysely } from "@schemavaults/dbh";
import { sql } from "@/sql";
export async function up(db: Kysely<any>): Promise<void> {
await sql`
CREATE TABLE IF NOT EXISTS EXAMPLE_SQUIRRELS (
squirrel_id UUID PRIMARY KEY,
squirrel_name TEXT NOT NULL,
created_at BIGINT NOT NULL
);
`.execute(db);
// Always interpolate values via ${...}; the sql tag parameterizes them.
await sql`CREATE INDEX squirrels_name_idx ON EXAMPLE_SQUIRRELS (squirrel_name);`.execute(
db,
);
}
export async function down(db: Kysely<any>): Promise<void> {
await sql`DROP TABLE IF EXISTS EXAMPLE_SQUIRRELS;`.execute(db);
}重要提示:迁移文件必须始终从导入@/sql,绝不能直接从sql导入。@schemavaults/dbh/sql步骤会将字面量build-db-migrations导入说明符重写为指向构建后的独立@/sql的相对路径,因此导入必须精确写为sql.js才能使构建正常工作。(这就是一次性设置要配置@/sql别名的原因。)@/sql
Empty template migration
空模板迁移
A no-op migration is valid (useful as a starting template):
ts
// 00000-template-migration.ts
import type { Kysely } from "@schemavaults/dbh";
export async function up(
db: Kysely<any>, // eslint-disable-line @typescript-eslint/no-unused-vars
): Promise<void> {}
export async function down(
db: Kysely<any>, // eslint-disable-line @typescript-eslint/no-unused-vars
): Promise<void> {}无操作迁移是有效的(可用作起始模板):
ts
// 00000-template-migration.ts
import type { Kysely } from "@schemavaults/dbh";
export async function up(
db: Kysely<any>, // eslint-disable-line @typescript-eslint/no-unused-vars
): Promise<void> {}
export async function down(
db: Kysely<any>, // eslint-disable-line @typescript-eslint/no-unused-vars
): Promise<void> {}Validating migrations
验证迁移
Before building or applying, assert your source migrations directory is
well-formed. The command checks all four rules
above and exits when valid, non-zero otherwise (good for CI / pre-commit):
validate-migration-directory0bash
bunx @schemavaults/dbh validate-migration-directory ./src/db/migrationsIt reports each problem with an / prefix:
[ERROR][WARN]- empty directory,
- a file missing the 5-digit prefix,
- a module missing or
up(),down() - duplicate migration numbers (branch collisions).
Treat duplicate numbers as warnings (non-fatal) with .
--duplicates-as-warningsMigrations importing through tsconfig path aliases (like from the
one-time setup) validate correctly: the command discovers the nearest
declaring by walking up from the
migrations directory (following ), and applies those aliases when
importing each module. Use to point at a specific config
instead of relying on discovery.
@/sqltsconfig.jsoncompilerOptions.pathsextends--tsconfig <path>在构建或应用之前,确认你的源码迁移目录格式正确。 命令会检查上述所有四条规则,验证通过时退出码为 ,否则为非零值(适用于 CI / 预提交检查):
validate-migration-directory0bash
bunx @schemavaults/dbh validate-migration-directory ./src/db/migrations它会以 / 前缀报告每个问题:
[ERROR][WARN]- 空目录
- 文件缺少5位前缀
- 模块缺少 或
up()down() - 迁移编号重复(分支冲突)
使用 可将重复编号视为警告(非致命错误)。
--duplicates-as-warnings通过 tsconfig 路径别名导入的迁移(例如一次性设置中的 )可以正确验证:该命令会从迁移目录向上遍历(遵循 ),找到最近的声明了 的 ,并在导入每个模块时应用这些别名。使用 可以指定特定的配置文件,而不依赖自动发现。
@/sqlextendscompilerOptions.pathstsconfig.json--tsconfig <path>Building migrations
构建迁移
TypeScript migrations must be compiled to JavaScript before they're applied
(the step runs on Node and imports ). The
command uses Bun's bundler and also builds the standalone module the
migrations depend on. Point at the local you created
during setup:
migrate.jsbuild-db-migrationssql--sql-modulesql.tsbash
bunx @schemavaults/dbh build-db-migrations ./src/db/migrations \
--outdir ./dist/migrations \
--sql-module ./src/db/sql.ts \
--sql-outdir ./distKey options:
- — directory of
<migrations-src>migration sources (positional)..ts - — where compiled
--outdir <dir>migrations are written (required)..js - — path to your local
--sql-module <path>module to build alongside (required).sql.ts - — where the built
--sql-outdir <dir>goes (defaults to the parent ofsql.js).--outdir - — packages to keep external (default:
--external <pkg...>,@schemavaults/dbh).kysely
build-db-migrationsbunTypeScript 迁移必须先编译为 JavaScript 才能应用( 步骤在 Node 上运行并导入 文件)。 命令使用 Bun 的打包器,同时也会构建迁移依赖的独立 模块。将 指向你在设置期间创建的本地 :
migrate.jsbuild-db-migrationssql--sql-modulesql.tsbash
bunx @schemavaults/dbh build-db-migrations ./src/db/migrations \
--outdir ./dist/migrations \
--sql-module ./src/db/sql.ts \
--sql-outdir ./dist关键选项:
- ——
<migrations-src>迁移源码目录(位置参数).ts - —— 编译后的
--outdir <dir>迁移文件输出目录(必填).js - —— 要一同构建的本地
--sql-module <path>模块路径(必填)sql.ts - —— 构建后的
--sql-outdir <dir>输出位置(默认为sql.js的父目录)--outdir - —— 不打包进产物、保持为外部依赖的包(默认:
--external <pkg...>、@schemavaults/dbh)kysely
build-db-migrationsbunRunning migrations
运行迁移
Apply built migrations with , and roll back with . Both take
the built migration folder and require an ; credentials come
from (or an ). Run these with (Node.js):
most PostgreSQL drivers target Node rather than Bun.
migratereverse--environmentprocess.env--env-filenpxbash
undefined使用 应用已构建的迁移,使用 回滚。两者都需要传入已构建的迁移文件夹,并且需要指定 ;凭证来自 (或 )。请使用 (Node.js)运行这些命令:大多数 PostgreSQL 驱动面向 Node 而非 Bun。
migratereverse--environmentprocess.env--env-filenpxbash
undefinedApply all pending migrations (to latest):
应用所有待执行的迁移(到最新版本):
npx @schemavaults/dbh migrate ./dist/migrations --environment production --env-file ./.env.production
npx @schemavaults/dbh migrate ./dist/migrations --environment production --env-file ./.env.production
Apply up to a specific version (the migration name w/o extension):
应用到指定版本(不带扩展名的迁移名称):
npx @schemavaults/dbh migrate ./dist/migrations 00001-create-users-table --environment staging
npx @schemavaults/dbh migrate ./dist/migrations 00001-create-users-table --environment staging
Roll back down to a target version:
回滚到目标版本:
npx @schemavaults/dbh reverse ./dist/migrations 00000-template-migration --environment staging
Options for `migrate` / `reverse`:
- `<folder>` — path to the built migration folder (positional).
- `[version]` / `<version>` — target migration name; `migrate` defaults to latest, `reverse` requires it.
- `-e, --environment <env>` — `development | test | staging | production` (required).
- `--ws-proxy-url <url>` — custom Neon-compatible WebSocket proxy URL.
- `--env-file <path>` — load DB credentials from a `.env` file first.
Each result line prints as `[Up|Down] <migrationName>: <Success|Error|NotExecuted>`.npx @schemavaults/dbh reverse ./dist/migrations 00000-template-migration --environment staging
`migrate` / `reverse` 的选项:
- `<folder>` —— 已构建的迁移文件夹路径(位置参数)
- `[version]` / `<version>` —— 目标迁移名称;`migrate` 默认为最新版本,`reverse` 必填
- `-e, --environment <env>` —— `development | test | staging | production`(必填)
- `--ws-proxy-url <url>` —— 自定义的兼容 Neon 的 WebSocket 代理 URL
- `--env-file <path>` —— 优先从 `.env` 文件加载数据库凭证
每行结果会以 `[Up|Down] <migrationName>: <Success|Error|NotExecuted>` 的格式打印。Programmatic API
编程式 API
The same operations are available from for tests or
custom scripts, using the adapter's Kysely instance:
@schemavaults/dbh/migratets
import { migrate, reverse } from "@schemavaults/dbh/migrate";
await migrate({ db: adapter.db, migrationFolder, version /* optional */ });
await reverse({ db: adapter.db, migrationFolder, version });相同的操作也可以通过 在测试或自定义脚本中使用,使用适配器的 Kysely 实例:
@schemavaults/dbh/migratets
import { migrate, reverse } from "@schemavaults/dbh/migrate";
await migrate({ db: adapter.db, migrationFolder, version /* optional */ });
await reverse({ db: adapter.db, migrationFolder, version });Typical end-to-end flow
典型端到端流程
bash
undefinedbash
undefined1. Validate the source migrations directory.
1. 验证源码迁移目录
bunx @schemavaults/dbh validate-migration-directory ./src/db/migrations
bunx @schemavaults/dbh validate-migration-directory ./src/db/migrations
2. Build .ts migrations (+ sql module) to .js.
2. 将 .ts 迁移(+ sql 模块)构建为 .js
bunx @schemavaults/dbh build-db-migrations ./src/db/migrations
--outdir ./dist/migrations --sql-module ./src/db/sql.ts --sql-outdir ./dist
--outdir ./dist/migrations --sql-module ./src/db/sql.ts --sql-outdir ./dist
bunx @schemavaults/dbh build-db-migrations ./src/db/migrations
--outdir ./dist/migrations --sql-module ./src/db/sql.ts --sql-outdir ./dist
--outdir ./dist/migrations --sql-module ./src/db/sql.ts --sql-outdir ./dist
3. Apply the built migrations (npx / Node.js — pg drivers target Node).
3. 应用已构建的迁移(npx / Node.js —— pg 驱动面向 Node)
npx @schemavaults/dbh migrate ./dist/migrations --environment production --env-file ./.env.production
undefinednpx @schemavaults/dbh migrate ./dist/migrations --environment production --env-file ./.env.production
undefinedRequired environment variables (for migrate/reverse)
必需的环境变量(用于 migrate/reverse)
POSTGRES_USERPOSTGRES_PASSWORDPOSTGRES_URLPOSTGRES_HOSTPOSTGRES_PORTPOSTGRES_DATABASEPOSTGRES_URL_NON_POOLINGSCHEMAVAULTS_DBH_DEBUG=truePOSTGRES_USERPOSTGRES_PASSWORDPOSTGRES_URLPOSTGRES_HOSTPOSTGRES_PORTPOSTGRES_DATABASEPOSTGRES_URL_NON_POOLINGSCHEMAVAULTS_DBH_DEBUG=true