何时使用
适用:
- 在新项目或现有项目中引入并配置 Drizzle ORM。
- 用 TypeScript 优先的方式设计数据库 schema(表、枚举、外键、索引)。
- 编写复杂关系查询(join、子查询、聚合)或使用
db.query.*嵌套查询。 - 配置或排查 Drizzle Kit 迁移。
- 接入 Next.js App Router、tRPC、Hono,或 Neon/Turso/PlanetScale/Supabase 等 Serverless 数据库。
- 优化性能:预编译语句、批量、连接池。
- 从 Prisma / TypeORM / Knex 迁移到 Drizzle。
不该用(负边界):
- 任务栈是 Prisma、TypeORM、Knex、Sequelize 等非 Drizzle ORM,或纯手写 SQL/原生驱动。
- 纯数据库运维/容量/索引调优而不涉及 Drizzle 代码层。
- 缺少 dialect、连接串、目标运行时等关键输入时,先停下来问清。
为什么用 Drizzle:TypeScript 优先、零运行时开销,直接编译成原生 SQL(不像 Prisma 依赖查询引擎二进制),适合边缘运行时与 Serverless;SQL 风格 API、全链路类型推断、类 Prisma 的关系查询 API 可避免 N+1。
步骤
- 安装并选定 dialect(postgresql / mysql / sqlite),明确目标运行时与数据库。
- 定义 schema(表、枚举、外键、索引),用
relations()声明表间关系。 - 用
InferSelectModel/InferInsertModel从 schema 推断类型,避免手写 interface。 - 创建数据库客户端,务必把
{ schema }传入drizzle()以启用db.query.*。 - 写
drizzle.config.ts,开发期用push快速试错,生产用generate+migrate。 - 查询:简单/聚合用 SQL 风格
db.select(),嵌套数据用关系 APIdb.query.*配with。 - 优化:高频查询用
prepare(),多条独立查询用db.batch(),schema 内声明index()。
指令
# 由 schema 变更生成迁移 SQL
npx drizzle-kit generate
# 直接推送 schema 到库(仅开发,跳过迁移文件,可能丢数据)
npx drizzle-kit push
# 执行待运行迁移(生产)
npx drizzle-kit migrate
# 打开 Drizzle Studio(GUI 浏览数据库)
npx drizzle-kit studio
示例
表与外键(pg-core):
import { pgTable, text, timestamp, boolean, uuid, pgEnum } from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
export const roleEnum = pgEnum("role", ["admin", "user", "moderator"]);
export const users = pgTable("users", {
id: uuid("id").defaultRandom().primaryKey(),
email: text("email").notNull().unique(),
name: text("name").notNull(),
role: roleEnum("role").default("user").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
export const posts = pgTable("posts", {
id: uuid("id").defaultRandom().primaryKey(),
title: text("title").notNull(),
published: boolean("published").default(false).notNull(),
authorId: uuid("author_id").references(() => users.id, { onDelete: "cascade" }).notNull(),
});
关系 + 类型推断:
import type { InferSelectModel, InferInsertModel } from "drizzle-orm";
export const usersRelations = relations(users, ({ many }) => ({ posts: many(posts) }));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
export type User = InferSelectModel<typeof users>;
export type NewUser = InferInsertModel<typeof users>;
查询(SQL 风格 + 关系 API):
import { eq, desc, count } from "drizzle-orm";
// join + 过滤 + 排序
const postsWithAuthors = await db
.select({ title: posts.title, authorName: users.name })
.from(posts)
.innerJoin(users, eq(posts.authorId, users.id))
.where(eq(posts.published, true))
.orderBy(desc(posts.createdAt));
// 关系 API:单次查询解析嵌套数据,避免 N+1
const usersWithPosts = await db.query.users.findMany({
with: { posts: { where: eq(posts.published, true), limit: 5 } },
});
写入与事务:
const [newUser] = await db.insert(users).values({ email, name }).returning();
const result = await db.transaction(async (tx) => {
const [user] = await tx.insert(users).values({ email, name }).returning();
await tx.insert(posts).values({ title: "Welcome", authorId: user.id });
return user;
});
客户端接入(务必传 { schema }):
// Neon Serverless (PostgreSQL)
import { drizzle } from "drizzle-orm/neon-http";
import { neon } from "@neondatabase/serverless";
import * as schema from "./schema";
export const db = drizzle(neon(process.env.DATABASE_URL!), { schema });
Turso/LibSQL 用 drizzle-orm/libsql + createClient;PlanetScale 用 drizzle-orm/planetscale-serverless + Client。
性能:
// 预编译语句:编译一次,多次执行
const getUserById = db.query.users
.findFirst({ where: eq(users.id, sql.placeholder("id")) })
.prepare("get_user_by_id");
const user = await getUserById.execute({ id: "abc-123" });
// 批量:多条独立查询一次往返
const [allUsers, recentPosts] = await db.batch([
db.select().from(users),
db.select().from(posts).orderBy(desc(posts.createdAt)).limit(10),
]);
注意事项
约定(Do):
- schema 集中放
db/schema.ts,或按域拆分db/schema/users.ts等。 - 用
InferSelectModel/InferInsertModel而非手写类型。 - 嵌套数据用
db.query.*关系 API 避免 N+1;高频查询用预编译语句。 - 生产用
generate+migrate;drizzle()必须传{ schema }。
禁忌(Don't):
- 生产环境别用
drizzle-kit push,可能造成数据丢失。 - 别在 Drizzle 查询构建器已支持的场景手写原生 SQL。
- 想用
with就别忘了定义relations()。 - Serverless 中别每请求新建连接,要用连接池。
排错:
db.query.tableName为 undefined:把所有 schema(含 relations)传入drizzle(client, { schema })。- schema 改动后迁移冲突:先
drizzle-kit generate再drizzle-kit migrate。 - MySQL 上
.returning()报类型错:MySQL 不支持RETURNING,改用.execute()并从结果读insertId。
边界提醒:输出不替代针对具体环境的校验、测试与专家评审;缺少必要输入、权限、安全边界或验收标准时应停下来确认。
互见
- 关联:TypeScript 类型推断、Next.js App Router 数据层、Serverless / 边缘运行时数据库接入。
- 迁移来源对照:Prisma / TypeORM / Knex 迁移到 Drizzle。
采编自 sickn33/antigravity-awesome-skills(MIT 许可)。