Drizzle ORM 是一个 TypeScript 优先的轻量级 ORM(Object-Relational Mapping)框架,专注于提供类型安全的数据库操作体验。相比传统 ORM,它采用接近 SQL 的 API 设计,在保持类型推导能力的同时避免了运行时性能损耗。本文将介绍 Drizzle ORM 的核心特性和使用方式,帮助你快速学习 Drizzle ORM
什么是 Drizzle ORM
Drizzle ORM 是一个为 TypeScript 开发者设计的现代化 ORM 框架,核心目标是在提供完整类型安全的同时保持接近原生 SQL 的开发体验。官方文档 提供了完整的 API 参考和使用指南。
核心特性
Drizzle ORM 的设计围绕以下几个核心特性展开:
类型安全(Type Safety)
Drizzle 提供端到端的类型推导。从 Schema 定义到查询结果,TypeScript 编译器能够在编译时捕获类型错误。
typescript
// Schema 定义自动推导出类型
const users = pgTable("users", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull(),
});
// 查询结果自动推导类型
const result = await db.select().from(users).where(eq(users.id, 1));
// result 类型: { id: number; name: string; email: string }[]
SQL-Like API
Drizzle 的 API 设计贴近 SQL 语法,降低学习成本。熟悉 SQL 的开发者可以快速上手。
typescript
// Drizzle API
await db.select().from(users).where(eq(users.email, "user@example.com"));
// 对应的 SQL
// SELECT * FROM users WHERE email = 'user@example.com'
零运行时开销(Zero Runtime Overhead)
Drizzle 不使用代理对象(Proxy)或运行时反射(Reflection),所有类型检查在编译时完成。查询构建器直接生成 SQL 字符串。参考 Drizzle 性能基准测试。
支持多种数据库:Drizzle ORM 支持主流关系型数据库,包括 PostgreSQL、MySQL、SQLite 等
Drizzle Kit:数据库迁移工具
Drizzle Kit 是 Drizzle ORM 的配套 CLI 工具,负责数据库迁移管理、Schema 同步和自省。详细的命令和配置选项可参考 Drizzle Kit 官方文档。
核心功能
- Schema 自省:从现有数据库反向生成 Drizzle Schema 代码
- 自动迁移生成:对比 Schema 定义和数据库状态,生成迁移 SQL
- 双向同步:支持从数据库拉取或推送到数据库
- 可视化工具:提供 Studio 在线管理数据库
常用命令
bash
# 从数据库拉取并生成 Schema(新版本,推荐)
npx drizzle-kit pull
# 推送 Schema 到数据库(开发环境)
npx drizzle-kit push
# 生成迁移文件(对比 Schema 和数据库)
npx drizzle-kit generate
# 执行迁移文件(生产环境)
npx drizzle-kit migrate
# 查看迁移状态
npx drizzle-kit check
# 启动可视化管理工具
npx drizzle-kit studio
典型工作流
bash
# 场景 1: 从零开始开发(Schema 优先)
npm run db:generate # 生成初始迁移
npm run db:push # 推送到数据库
# 场景 2: 接手现有项目(数据库优先)
npx drizzle-kit pull # 从数据库拉取 Schema
npm run dev # 开始开发
# 场景 3: Schema 变更后的开发流程
# 修改 schema.ts
npm run db:generate # 生成迁移文件
npm run db:push # 推送到开发数据库
# 场景 4: 生产环境部署
npm run db:generate # 生成迁移文件
git add drizzle/ # 版本控制
npm run db:migrate # 在生产环境执行迁移
push vs generate+migrate
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
push |
快速、简单 | 无迁移历史、难以回滚 | 开发环境 |
generate+migrate |
可审查、可版本控制、可回滚 | 步骤多、需要管理迁移文件 | 生产环境 |
配置文件示例
typescript
import type { Config } from "drizzle-kit";
export default {
schema: "./src/db/schema.ts", // Schema 文件路径
out: "./drizzle", // 迁移文件输出目录
dialect: "postgresql", // 数据库类型:postgresql | mysql | sqlite
dbCredentials: {
url: process.env.DATABASE_URL!, // 数据库连接字符串
},
} satisfies Config;
核心功能
本章深入介绍 Drizzle ORM 的核心功能,包括查询构建、关系处理、事务管理和类型系统。
查询构建器
Drizzle 提供完整的查询构建器 API,支持复杂的查询操作。参考 Queries 官方文档 了解更多查询选项。
基础查询
typescript
import { db } from "./db";
import { users, posts } from "./db/schema";
import { eq, gt, and, or, like } from "drizzle-orm";
// 条件查询
const activeUsers = await db.select().from(users).where(eq(users.status, "active"));
// 多条件组合
const filteredUsers = await db
.select()
.from(users)
.where(and(gt(users.age, 18), like(users.email, "%@example.com")));
// 排序和分页
const paginatedUsers = await db.select().from(users).orderBy(users.createdAt).limit(10).offset(20);
JOIN 查询
typescript
// INNER JOIN
const usersWithPosts = await db
.select({
userId: users.id,
userName: users.name,
postTitle: posts.title,
})
.from(users)
.innerJoin(posts, eq(users.id, posts.authorId));
// LEFT JOIN
const allUsersWithPosts = await db.select().from(users).leftJoin(posts, eq(users.id, posts.authorId));
聚合查询
typescript
import { count, avg, sum } from "drizzle-orm";
// 统计数量
const userCount = await db.select({ count: count() }).from(users);
// 分组统计
const postsByAuthor = await db
.select({
authorId: posts.authorId,
postCount: count(posts.id),
})
.from(posts)
.groupBy(posts.authorId);
关系查询
Drizzle 支持声明式关系定义,简化关联数据查询。详细的关系查询 API 可参考 Relational Queries 文档。
定义关系
typescript
import { relations } from "drizzle-orm";
// 在 schema.ts 中定义关系
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
查询关联数据
typescript
// 查询用户及其所有文章
const userWithPosts = await db.query.users.findFirst({
where: eq(users.id, 1),
with: {
posts: true,
},
});
// 嵌套查询
const postWithAuthor = await db.query.posts.findFirst({
where: eq(posts.id, 1),
with: {
author: true,
},
});
// 多级嵌套
const userWithPostsAndComments = await db.query.users.findFirst({
with: {
posts: {
with: {
comments: true,
},
},
},
});
事务处理
Drizzle 提供事务支持,确保操作的原子性。更多事务用法参考 Transactions 文档。
typescript
// 基础事务
await db.transaction(async (tx) => {
// 创建用户
const [newUser] = await tx
.insert(users)
.values({
name: "Bob",
email: "bob@example.com",
})
.returning();
// 为用户创建文章
await tx.insert(posts).values({
title: "First Post",
content: "Hello World",
authorId: newUser.id,
});
});
// 事务回滚
try {
await db.transaction(async (tx) => {
await tx.insert(users).values({ name: "Alice", email: "alice@example.com" });
// 如果这里抛出错误,整个事务会回滚
throw new Error("Something went wrong");
});
} catch (error) {
console.log("Transaction rolled back");
}
迁移管理
Drizzle Kit 提供迁移管理功能,支持增量迁移和版本控制。详细的迁移命令和配置可参考 Migrations 文档。
生成迁移
bash
# 根据 schema 变化生成迁移文件
npx drizzle-kit generate:pg
# 查看迁移状态
npx drizzle-kit status
# 应用迁移
npx drizzle-kit push:pg
迁移文件示例
生成的迁移文件是标准 SQL:
sql
-- drizzle/0000_initial.sql
CREATE TABLE IF NOT EXISTS "users" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"email" text NOT NULL,
"created_at" timestamp DEFAULT now()
);
CREATE UNIQUE INDEX IF NOT EXISTS "users_email_idx" ON "users" ("email");
类型推导
Drizzle 的类型系统提供完整的端到端类型安全,充分利用了 TypeScript 的类型推导能力。更多类型推导的用法可参考 Goodies 文档。
typescript
// 从 Schema 推导类型
type User = typeof users.$inferSelect; // 查询结果类型
type NewUser = typeof users.$inferInsert; // 插入数据类型
// 查询结果自动推导
const result = await db
.select({
id: users.id,
name: users.name,
})
.from(users);
// result 类型: { id: number; name: string }[]
// 部分选择也有类型推导
const partial = await db
.select({
userName: users.name,
postTitle: posts.title,
})
.from(users)
.leftJoin(posts, eq(users.id, posts.authorId));
// partial 类型: { userName: string; postTitle: string | null }[]
类型推导覆盖所有操作,编辑器可以提供准确的自动补全和错误提示。
实战案例:构建任务管理系统
本章通过一个任务管理系统的完整实战案例,展示如何使用 Drizzle ORM 从零开始构建一个真实应用,包括项目初始化、数据库设计、Schema 定义、迁移管理以及常见的业务查询操作。
案例背景
我们要构建一个团队任务管理系统,需要实现以下核心功能:
- 用户管理和认证
- 项目创建和组织
- 任务分配和状态跟踪
- 任务标签分类
- 任务评论和协作
这个案例将完整演示 Drizzle ORM 的实际应用场景,包括多对多关系、外键约束、复杂查询等。
项目初始化
第 1 步:创建项目并安装依赖
bash
# 创建项目目录
mkdir task-manager && cd task-manager
初始化项目:npm init -y + 编辑 package.json
json
{
"name": "task-manager",
"version": "1.0.0",
"type": "module",
"scripts": {
"db:generate": "drizzle-kit generate",
"db:push": "drizzle-kit push",
"db:seed": "tsx src/seed.ts",
"db:query": "tsx src/query.ts"
},
"dependencies": {
"dotenv": "^17.2.3",
"drizzle-orm": "^0.45.1",
"postgres": "^3.4.7"
},
"devDependencies": {
"@types/node": "^25.0.0",
"drizzle-kit": "^0.31.8",
"tsx": "^4.21.0",
"typescript": "^5.9.3"
}
}
初始化 TypeScript 配置:npx tsc --init + 编辑 tsconfig.json
json
{
// Visit https://aka.ms/tsconfig to read more about this file
"compilerOptions": {
"module": "esnext",
"moduleResolution": "bundler",
"types": ["node"],
"target": "esnext"
}
}
第 2 步:配置环境变量
创建 .env 文件:
env
DATABASE_URL=postgresql://postgres:password@localhost:5432/task_manager_db
第 3 步:创建 PostgreSQL 数据库
使用 PostgreSQL 创建数据库:
bash
# 连接到 PostgreSQL
psql -U postgres
# 创建数据库
CREATE DATABASE task_manager_db;
数据库设计
ER 关系说明
- 一个用户可以创建多个项目(一对多)
- 一个项目可以包含多个任务(一对多)
- 一个任务可以分配给一个用户(多对一)
- 一个任务可以有多个标签(多对多)
- 一个任务可以有多条评论(一对多)
定义 Schema
创建 src/db/schema.ts 文件:
typescript
import { pgTable, serial, text, varchar, integer, timestamp, boolean, pgEnum, primaryKey } from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
// 定义任务状态枚举
export const taskStatusEnum = pgEnum("task_status", ["todo", "in_progress", "review", "completed"]);
// 定义任务优先级枚举
export const taskPriorityEnum = pgEnum("task_priority", ["low", "medium", "high", "urgent"]);
// 1. 用户表
export const users = pgTable("users", {
id: serial("id").primaryKey(),
username: varchar("username", { length: 50 }).notNull().unique(),
email: varchar("email", { length: 100 }).notNull().unique(),
passwordHash: varchar("password_hash", { length: 255 }).notNull(),
avatar: varchar("avatar", { length: 255 }),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// 2. 项目表
export const projects = pgTable("projects", {
id: serial("id").primaryKey(),
name: varchar("name", { length: 100 }).notNull(),
description: text("description"),
ownerId: integer("owner_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
isActive: boolean("is_active").default(true).notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at"),
});
// 3. 任务表
export const tasks = pgTable("tasks", {
id: serial("id").primaryKey(),
title: varchar("title", { length: 200 }).notNull(),
description: text("description"),
projectId: integer("project_id")
.notNull()
.references(() => projects.id, { onDelete: "cascade" }),
assigneeId: integer("assignee_id").references(() => users.id, {
onDelete: "set null",
}),
status: taskStatusEnum("status").default("todo").notNull(),
priority: taskPriorityEnum("priority").default("medium").notNull(),
dueDate: timestamp("due_date"),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at"),
});
// 4. 标签表
export const tags = pgTable("tags", {
id: serial("id").primaryKey(),
name: varchar("name", { length: 50 }).notNull().unique(),
color: varchar("color", { length: 7 }).default("#3b82f6"),
});
// 5. 任务标签关联表(多对多)
export const taskTags = pgTable(
"task_tags",
{
taskId: integer("task_id")
.notNull()
.references(() => tasks.id, { onDelete: "cascade" }),
tagId: integer("tag_id")
.notNull()
.references(() => tags.id, { onDelete: "cascade" }),
},
(table) => ({
pk: primaryKey({ columns: [table.taskId, table.tagId] }),
})
);
// 6. 评论表
export const comments = pgTable("comments", {
id: serial("id").primaryKey(),
taskId: integer("task_id")
.notNull()
.references(() => tasks.id, { onDelete: "cascade" }),
userId: integer("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
content: text("content").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// 定义关系
export const usersRelations = relations(users, ({ many }) => ({
ownedProjects: many(projects),
assignedTasks: many(tasks),
comments: many(comments),
}));
export const projectsRelations = relations(projects, ({ one, many }) => ({
owner: one(users, {
fields: [projects.ownerId],
references: [users.id],
}),
tasks: many(tasks),
}));
export const tasksRelations = relations(tasks, ({ one, many }) => ({
project: one(projects, {
fields: [tasks.projectId],
references: [projects.id],
}),
assignee: one(users, {
fields: [tasks.assigneeId],
references: [users.id],
}),
taskTags: many(taskTags),
comments: many(comments),
}));
export const tagsRelations = relations(tags, ({ many }) => ({
taskTags: many(taskTags),
}));
export const taskTagsRelations = relations(taskTags, ({ one }) => ({
task: one(tasks, {
fields: [taskTags.taskId],
references: [tasks.id],
}),
tag: one(tags, {
fields: [taskTags.tagId],
references: [tags.id],
}),
}));
export const commentsRelations = relations(comments, ({ one }) => ({
task: one(tasks, {
fields: [comments.taskId],
references: [tasks.id],
}),
user: one(users, {
fields: [comments.userId],
references: [users.id],
}),
}));
配置数据库连接
创建 src/db/index.ts 文件:
typescript
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import * as schema from "./schema";
import "dotenv/config";
// 创建 PostgreSQL 连接
const client = postgres(process.env.DATABASE_URL!);
// 创建 Drizzle 实例
export const db = drizzle(client, { schema });
配置迁移管理
创建 drizzle.config.ts 文件:
typescript
import type { Config } from "drizzle-kit";
import "dotenv/config";
export default {
schema: "./src/db/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
} satisfies Config;
执行数据库迁移
bash
# 生成迁移文件
npx drizzle-kit generate
# 查看生成的 SQL 文件
cat drizzle/0000_*.sql
# 执行迁移(将 Schema 同步到数据库)
npx drizzle-kit push
# 或者使用 migrate 命令
npx drizzle-kit migrate
执行后会在 drizzle 目录生成迁移 SQL 文件,类似:
sql
-- drizzle/0000_initial.sql
CREATE TYPE "task_status" AS ENUM('todo', 'in_progress', 'review', 'completed');
CREATE TYPE "task_priority" AS ENUM('low', 'medium', 'high', 'urgent');
CREATE TABLE IF NOT EXISTS "users" (
"id" serial PRIMARY KEY NOT NULL,
"username" varchar(50) NOT NULL UNIQUE,
"email" varchar(100) NOT NULL UNIQUE,
"password_hash" varchar(255) NOT NULL,
"avatar" varchar(255),
"created_at" timestamp DEFAULT now() NOT NULL
);
-- ... 其他表的创建语句
插入测试数据
创建 src/seed.ts 文件:
typescript
import { db } from "./db";
import { users, projects, tasks, tags, taskTags, comments } from "./db/schema";
async function seed() {
console.log("Seeding database...");
// 插入用户
const [user1, user2, user3] = await db
.insert(users)
.values([
{
username: "alice",
email: "alice@taskmanager.com",
passwordHash: "hash1",
avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=alice",
},
{
username: "bob",
email: "bob@taskmanager.com",
passwordHash: "hash2",
avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=bob",
},
{
username: "charlie",
email: "charlie@taskmanager.com",
passwordHash: "hash3",
avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=charlie",
},
])
.returning();
if (!user1 || !user2 || !user3) throw new Error("Failed to create users");
console.log("✓ Users created");
// 插入项目
const [project1, project2] = await db
.insert(projects)
.values([
{
name: "Website Redesign",
description: "Redesign company website with modern UI/UX",
ownerId: user1.id,
},
{
name: "Mobile App Development",
description: "Build iOS and Android mobile application",
ownerId: user2.id,
},
])
.returning();
if (!project1 || !project2) throw new Error("Failed to create projects");
console.log("✓ Projects created");
// 插入任务
const [task1, task2, task3, task4] = await db
.insert(tasks)
.values([
{
title: "Design homepage mockup",
description: "Create high-fidelity mockup for homepage",
projectId: project1.id,
assigneeId: user1.id,
status: "in_progress",
priority: "high",
dueDate: new Date("2025-12-20"),
},
{
title: "Implement authentication",
description: "Add JWT-based authentication system",
projectId: project1.id,
assigneeId: user2.id,
status: "todo",
priority: "urgent",
dueDate: new Date("2025-12-15"),
},
{
title: "Setup CI/CD pipeline",
description: "Configure GitHub Actions for automated deployment",
projectId: project1.id,
assigneeId: user3.id,
status: "completed",
priority: "medium",
},
{
title: "Design app architecture",
description: "Define mobile app architecture and tech stack",
projectId: project2.id,
assigneeId: user2.id,
status: "review",
priority: "high",
dueDate: new Date("2025-12-18"),
},
])
.returning();
if (!task1 || !task2 || !task3 || !task4) throw new Error("Failed to create tasks");
console.log("✓ Tasks created");
// 插入标签
const [tag1, tag2, tag3, tag4, tag5] = await db
.insert(tags)
.values([
{ name: "frontend", color: "#3b82f6" },
{ name: "backend", color: "#10b981" },
{ name: "design", color: "#f59e0b" },
{ name: "devops", color: "#8b5cf6" },
{ name: "mobile", color: "#ec4899" },
])
.returning();
if (!tag1 || !tag2 || !tag3 || !tag4 || !tag5) throw new Error("Failed to create tags");
console.log("✓ Tags created");
// 插入任务标签关联
await db.insert(taskTags).values([
{ taskId: task1.id, tagId: tag1.id }, // Design homepage - frontend
{ taskId: task1.id, tagId: tag3.id }, // Design homepage - design
{ taskId: task2.id, tagId: tag2.id }, // Authentication - backend
{ taskId: task3.id, tagId: tag4.id }, // CI/CD - devops
{ taskId: task4.id, tagId: tag5.id }, // App architecture - mobile
]);
console.log("✓ Task tags created");
// 插入评论
await db.insert(comments).values([
{
taskId: task1.id,
userId: user2.id,
content: "Looking great! Please add dark mode support.",
},
{
taskId: task1.id,
userId: user3.id,
content: "Should we use Figma for the mockup?",
},
{
taskId: task2.id,
userId: user1.id,
content: "Don't forget to implement refresh token rotation.",
},
{
taskId: task4.id,
userId: user1.id,
content: "Consider using React Native for cross-platform development.",
},
]);
console.log("✓ Comments created");
console.log("\n✅ Database seeded successfully!");
process.exit(0);
}
seed().catch((error) => {
console.error("❌ Seeding failed:", error);
process.exit(1);
});
运行数据填充:
bash
npm run db:seed
常用查询和操作示例
创建 src/query.ts 文件,演示核心功能:
typescript
import { db } from "./db";
import { users, projects, tasks, tags, taskTags, comments } from "./db/schema";
import { eq, and, or, desc, count } from "drizzle-orm";
async function main() {
console.log("=== Drizzle ORM 实战示例 ===\n");
// 1. 查询所有用户
console.log("1. 查询所有用户:");
const allUsers = await db.select().from(users);
console.log(allUsers);
// 2. 查询用户创建的项目(关系查询)
console.log("\n2. 查询 Alice 创建的项目:");
const aliceProjects = await db.query.users.findFirst({
where: eq(users.username, "alice"),
with: {
ownedProjects: {
columns: {
id: true,
name: true,
description: true,
},
},
},
});
console.log(JSON.stringify(aliceProjects, null, 2));
// 3. 查询项目的任务及负责人(JOIN 查询)
console.log("\n3. 查询项目 1 的任务及负责人:");
const projectTasks = await db
.select({
taskId: tasks.id,
taskTitle: tasks.title,
status: tasks.status,
priority: tasks.priority,
assignee: users.username,
})
.from(tasks)
.leftJoin(users, eq(tasks.assigneeId, users.id))
.where(eq(tasks.projectId, 1));
console.log(projectTasks);
// 4. 查询任务及其标签(多对多关系)
console.log("\n4. 查询任务 1 的详细信息及标签:");
const taskWithTags = await db.query.tasks.findFirst({
where: eq(tasks.id, 1),
with: {
assignee: {
columns: {
username: true,
email: true,
},
},
taskTags: {
with: {
tag: true,
},
},
},
});
console.log(JSON.stringify(taskWithTags, null, 2));
// 5. 统计每个项目的任务数量(聚合查询)
console.log("\n5. 统计各项目的任务数量:");
const taskCounts = await db
.select({
projectId: projects.id,
projectName: projects.name,
taskCount: count(tasks.id),
})
.from(projects)
.leftJoin(tasks, eq(projects.id, tasks.projectId))
.groupBy(projects.id, projects.name);
console.log(taskCounts);
// 6. 更新操作:将任务 1 状态改为 completed
console.log("\n6. 更新任务状态:");
const [updatedTask] = await db
.update(tasks)
.set({
status: "completed",
updatedAt: new Date(),
})
.where(eq(tasks.id, 1))
.returning();
console.log(`✓ 任务 ${updatedTask.id} 状态已更新为: ${updatedTask.status}`);
// 7. 插入操作:添加新评论
console.log("\n7. 添加新评论:");
const [newComment] = await db
.insert(comments)
.values({
taskId: 1,
userId: 2,
content: "Great job on completing this task!",
})
.returning();
console.log(`✓ 评论已创建,ID: ${newComment.id}`);
// 8. 使用事务:创建项目并同时创建任务
console.log("\n8. 使用事务创建项目和任务:");
const result = await db.transaction(async (tx) => {
// 创建项目
const [newProject] = await tx
.insert(projects)
.values({
name: "Testing Project",
description: "A project for testing transactions",
ownerId: 1,
})
.returning();
// 为项目创建任务
const [newTask] = await tx
.insert(tasks)
.values({
title: "Setup testing environment",
projectId: newProject.id,
assigneeId: 2,
status: "todo",
priority: "high",
})
.returning();
return { project: newProject, task: newTask };
});
console.log(`✓ 项目创建成功: ${result.project.name}`);
console.log(`✓ 任务创建成功: ${result.task.title}`);
console.log("\n=== 示例执行完成 ===");
process.exit(0);
}
main().catch((error) => {
console.error("❌ 执行出错:", error);
process.exit(1);
});
运行查询示例:
bash
npm run db:query
Schema 变更与数据迁移
在实际开发中,需求经常变化,表结构也需要随之调整。下面演示如何使用 Drizzle Kit 处理 Schema 变更。
场景:产品经理要求为任务添加「预估工时」功能
第 1 步:修改 Schema 定义
编辑 src/db/schema.ts,在 tasks 表中添加 estimatedHours 字段:
typescript
// 3. 任务表
export const tasks = pgTable("tasks", {
id: serial("id").primaryKey(),
title: varchar("title", { length: 200 }).notNull(),
description: text("description"),
projectId: integer("project_id")
.notNull()
.references(() => projects.id, { onDelete: "cascade" }),
assigneeId: integer("assignee_id").references(() => users.id, {
onDelete: "set null",
}),
status: taskStatusEnum("status").default("todo").notNull(),
priority: taskPriorityEnum("priority").default("medium").notNull(),
dueDate: timestamp("due_date"),
estimatedHours: integer("estimated_hours"), // 新增:预估工时(小时)
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at"),
});
第 2 步:生成迁移文件
bash
npm run db:generate
查看生成的 SQL:
bash
cat drizzle/0001_*.sql
内容:
sql
ALTER TABLE "tasks" ADD COLUMN "estimated_hours" integer;
第 3 步:执行迁移
bash
npm run db:push
第 4 步:验证迁移结果
bash
psql -U postgres -d task_manager_db -c "\d tasks"
你将看到 estimated_hours 列已添加到表中。