Drizzle ORM — 轻量 SQL-like ORM
已复核Drizzle ORM 是一份 TypeScript 写的 SQL 层:schema 用普通 TS 对象声明,查询用链式 builder 拼成 SQL AST,再交给各 driver 执行。日常类比:它给你一本可组合的菜谱,而不是一排成品按钮——你看得见 SELECT / WHERE / JOIN,也要自己对 SQL 形状负责。
import { drizzle } from "drizzle-orm/node-postgres"import { eq } from "drizzle-orm"import { users } from "./schema"
const db = drizzle(pool)const rows = await db.select().from(users).where(eq(users.active, true))固定 0.45.2 的 drizzle-orm 包声明 sideEffects: false、零运行时依赖;PostgreSQL / MySQL / SQLite / SingleStore / Gel 等方言各自一份 core,driver 走独立入口。
不理解 Drizzle 的分层,下面这些事会对不上:
- 为什么改表结构不必跑客户端 codegen,但 migration SQL 仍要单独生成
- 为什么
db.query.users.findMany({ with: { posts: true } })只有把schema传进drizzle()才存在 - 为什么换 Cloudflare D1 或
postgres客户端时,query 代码能复用,import 路径却必须换 - 为什么 README 写的 bundle 体积不能直接当成你项目里的产物大小
固定版本可以拆成四层:
-
Schema 就是 TS 对象:
pgTable('users', { id: serial('id').primaryKey(), email: text('email').notNull() })返回带列属性的 table。serial()构造时已经把notNull和hasDefault设为 true;$inferSelect/$inferInsert从同一份 table 推出读写类型。 -
SQL builder 对应子句:
db.select().from(users).where(eq(users.id, 1))每一步往 config 里塞子句。eq通过bindIfParam把右侧值收成Param,序列化时走占位符,不是字符串拼接。 -
Then 触发执行:select/insert/update 都是
QueryPromise。await才让 dialect 把 AST 编成 SQL,再交给 session/driver。没有隐藏的 query engine 进程。 -
关系查询是第二条入口:传入
schema后,PgDatabase按表名挂db.query[tableName]。findMany/findFirst走RelationalQueryBuilder,用with描述嵌套,而不是手写leftJoin。没传 schema 时,类型会提示 generic 缺失。
案例 1:用 pg-core 定义表
Section titled “案例 1:用 pg-core 定义表”import { pgTable, serial, text } from "drizzle-orm/pg-core"
export const users = pgTable("users", { id: serial("id").primaryKey(), email: text("email").notNull(), bio: text("bio"),})
type User = typeof users.$inferSelect// { id: number; email: string; bio: string | null }逐部分解释:pgTable 把列 builder build 到 table 上;未标 notNull 的 text 在 select 类型里是 string | null。列名既可以写在 serial('id') 里,也可以留给 table 的 key 去 setName。
案例 2:SQL-like 查询
Section titled “案例 2:SQL-like 查询”import { eq } from "drizzle-orm"
const list = await db.select().from(users).where(eq(users.id, 1))空 select() 选出该表全部列。where 接收 SQL 节点;1 被绑成参数。需要表达式时用 sql<string>`lower(${users.email})`。
案例 3:关系查询与 drizzle-kit
Section titled “案例 3:关系查询与 drizzle-kit”const db = drizzle(pool, { schema })const withPosts = await db.query.users.findMany({ with: { posts: true } })findFirst 会把 limit: 1 写进同一套 relational config。配套 CLI 在同仓 drizzle-kit@0.31.10:generate 从 schema 差出 SQL 文件,migrate 按方言连接后应用,push 直接改库。studio 在本机起 HTTPS 服务(默认 0.0.0.0:4983),终端打印的入口是 https://local.drizzle.studio。Gel 方言在 generate / migrate / studio 上会直接退出。
- 把 RQB 当成默认 API:
db.query只在构造时拿到 relational schema 才会按表挂 builder;只传 client、不传schema时这条路不存在。 - 换 driver 只改连接串:
drizzle-orm/node-postgres、drizzle-orm/postgres-js、drizzle-orm/d1、drizzle-orm/neon-http等各自constructsession。schema/query 可复用,入口模块不能混。 - 把 Studio 写成必须登录官方云控制台:固定 kit 起的是本地服务,UI 域名是
local.drizzle.studio;这与「远程托管数据库浏览器」不是同一条合同。 - 把 README 的 ~7.4kb 或「零依赖」写成你的产物保证:
package.json标了sideEffects: false,最终体积仍取决于 import、bundler 和选中的 driver。 - 以为 0.45.2 就是仓库最新标签:同仓已有
v1.0.0-rc.4预发布标签;本文不绑定 RC 行为。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- 希望 schema 留在 TypeScript、查询形状接近 SQL 的 TS 服务
- 需要同一套 schema 接到多种 JS 运行时 driver(Node pool、serverless HTTP、D1 等)
- 能接受 migration 由 kit 生成 SQL、运行时不再做 codegen
不适用:
- 团队不打算读 SQL,只要对象 API 屏蔽方言差异
- 需要 Gel 的 kit generate/migrate/studio——固定版本这三条命令会拒绝
- 要把未实测的 bundle / 编译时长写成选型结论
- 准备跟 1.0 RC 走,却仍按 0.45.2 文档推理
固定版本边界
Section titled “固定版本边界”- 本文绑定
drizzle-team/drizzle-orm@273c7807...,drizzle-orm包版本0.45.2;npm latest 同号,未暴露gitHead。 - 同提交里
drizzle-kit是0.31.10(MIT),ORM 本体是 Apache-2.0。 - 仓库另有
v1.0.0-rc.4(2026-06-27,prerelease);升级前需重新固定 revision。 - 本文未安装依赖、未连数据库、未跑 kit/studio、未测 bundle,状态保持
UNVERIFIED。
- 「无 codegen」只覆盖客户端——类型从 mapped type 推,migration SQL 仍是独立产物。
- 两条查询入口合同不同——SQL builder 始终可用;RQB 依赖构造期 schema。
- driver 是模块边界,不是配置开关——换运行时先换 import。
- CLI 与 ORM 版本要一起读——kit 命令、Studio 入口和方言拒绝列表都以固定提交为准。
drizzle(pool)之后,db.query.users.findMany()一定可用吗?eq(users.id, 1)会把1嵌进 SQL 字符串吗?- 固定 kit 的
studio是不是必须连 drizzle.team 云端才能看表?
检查点:
- 不一定。没把
schema传给drizzle()时,query不会按表挂上 RQB。 - 不会。
eq把右侧收成参数化Param。 - 不是。kit 起本地 HTTPS 服务,打印的是
https://local.drizzle.studio。
- 文档:orm.drizzle.team
- 固定源码:drizzle-team/drizzle-orm —— 本文绑定提交
273c78071d4841b497f5144734b38294df7ec64b - postgres-js —— 常见的轻量 pg 客户端,对应
drizzle-orm/postgres-js - prisma —— DSL + 客户端 codegen 的对照路线
- kysely —— 更贴近裸 SQL 的 TS builder,不管 schema-as-code
- prisma —— schema-first + generate 的对照
- kysely —— 纯 query builder,不内置 schema 运行时
- typeorm —— Decorator / Active Record 一派
- postgresql ——
pg-core的主力方言 - postgres-js —— 常与 Drizzle 搭配的 Node pg 客户端
- codd-1979-extending —— Codd 1979 — 给关系模型补上”语义”
- auth-js —— Auth.js — 让 OAuth 登录和会话存储变成两个抽象
- better-auth —— better-auth — 把登录/OAuth/2FA/Passkey 拼成一行配置的 TS 认证框架
- kysely —— Kysely — TypeScript SQL 查询构建器
- mikro-orm —— MikroORM — Data Mapper Identity Map ORM
- postgres-js —— postgres.js — 写 SQL 但语法层就防注入的 Node 客户端
- prisma —— Prisma — 类型安全 ORM
- sequelize —— Sequelize — 老牌 Node ORM
- typeorm —— TypeORM — Decorator-based ORM
- unstorage —— unstorage — 让 KV 存储不绑死运行时的统一抽象层