Chapter 03

drizzle-kit:迁移工作流

用官方 CLI 把 TypeScript schema 的变化,一步步转成可回溯的 SQL 迁移文件,部署到各个环境。

3.1 drizzle-kit 做什么

drizzle-kit 是独立的 dev 依赖——对比你的 schema 和数据库当前状态,生成 SQL diff。四个核心命令:

generate
根据 schema 变化生成一份 SQL 迁移文件到 drizzle/ 目录(不真正改数据库)。提交到 git 作为历史。
migrate
把尚未应用的迁移文件按顺序应用到数据库。生产环境用这个。
push
不生成迁移文件,直接把 schema 推到数据库。仅推荐开发原型期——快但不可回溯。
studio
启动本地 GUI,浏览器打开查看/编辑数据。

3.2 drizzle.config.ts

drizzle-kit 的配置文件,放项目根:

import { defineConfig } from "drizzle-kit";

export default defineConfig({
  dialect: "postgresql",              // "postgresql" | "mysql" | "sqlite" | "turso"
  schema: "./src/schema.ts",           // 或 "./src/schema/**/*.ts"
  out: "./drizzle",                    // 迁移文件输出目录
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
  verbose: true,
  strict: true,                         // 破坏性变更前确认
});

3.3 基本工作流

步骤 1:改 schema.ts

export const users = pgTable("users", {
  id: serial().primaryKey(),
  name: text().notNull(),
+  email: text().notNull().unique(),    // 新增
});

步骤 2:生成迁移

$ npx drizzle-kit generate
# 产出 drizzle/0001_add_email.sql:
ALTER TABLE "users" ADD COLUMN "email" text NOT NULL;
ALTER TABLE "users" ADD CONSTRAINT "users_email_unique" UNIQUE("email");

同时生成元数据 meta/0001_snapshot.json——下次 diff 的参照。

步骤 3:应用迁移

方式 A:用 drizzle-kit CLI(适合 dev)

$ npx drizzle-kit migrate

方式 B:在应用代码里(推荐,适合 CI/生产)

// src/migrate.ts
import { migrate } from "drizzle-orm/postgres-js/migrator";
import { db } from "./db";

await migrate(db, { migrationsFolder: "./drizzle" });
console.log("migrated");

部署启动脚本先跑这个——保证生产库 schema 永远与代码一致。

3.4 破坏性变更:列重命名

drizzle-kit 无法自动判断"把列 name 改成 full_name"是重命名还是删 name 加 full_name。它会交互式问你

$ npx drizzle-kit generate
# ℹ column renamed from 'name' to 'full_name'?
#    1) +  full_name  (create column)
#    2) ~  name → full_name  (rename)
#    (use arrow keys)

选 rename,生成的 SQL 就是 ALTER TABLE ... RENAME COLUMN——保留数据。

3.5 push 模式:原型阶段神器

早期原型 schema 一天改 20 次,每次都生成迁移文件太繁琐:

$ npx drizzle-kit push

直接把当前 schema diff 推到数据库,不产 sql 文件。适合:

push 不适合生产

没有迁移文件 = 不可审计、不可回滚、无法多实例协同。团队项目一定用 generate + migrate 两步流程。

3.6 introspect:从现有数据库反向生成 schema

接手老项目、库里已有表——不想手写 schema?

$ npx drizzle-kit pull
# 扫描 dbCredentials.url 指向的库,在 out 目录生成:
#   schema.ts   ← 完整 schema 定义
#   relations.ts ← 关系定义
#   0000_initial.sql ← 同步的迁移

3.7 Drizzle Studio

$ npx drizzle-kit studio
# Drizzle Studio is up and running on https://local.drizzle.studio

本地 GUI 功能:浏览所有表、过滤/排序、原地改字段、执行自定义 SQL、看查询计划。数据和连接都在本机——不上传到任何云。

3.8 多 schema 文件

大项目表多、分模块:

src/schema/
├── users.ts
├── posts.ts
├── comments.ts
└── index.ts   // export * from 每一份
// drizzle.config.ts
schema: "./src/schema/**/*.ts",

3.9 多环境配置

export default defineConfig({
  dialect: "postgresql",
  schema: "./src/schema/*",
  out: "./drizzle",
  dbCredentials: { url: process.env.DATABASE_URL! },

  // 迁移时跳过某些表
  schemaFilter: ["public"],             // Postgres schema 过滤
  tablesFilter: ["app_*"],              // 只管前缀表

  // 多方言场景:生成两套迁移
  migrations: { prefix: "timestamp" },   // 或 "index"
});

3.10 CI 最佳实践

# .github/workflows/ci.yml 片段
- name: Check migration drift
  run: npx drizzle-kit check   # 确认没有漏 generate 的 schema 改动

- name: Apply migrations
  run: bun run src/migrate.ts
  env:
    DATABASE_URL: ${{ secrets.DATABASE_URL }}

drizzle-kit check 核对:"当前 schema.ts 生成的 diff,是否已经都进了 drizzle/ 目录?"——没进就 CI 失败,避免合并漏迁移。

3.11 回滚与数据修复

Drizzle 不自动生成 down 迁移(如 Rails 那种 up/down)。实践上:

3.12 小结