Drizzle 轻量 TypeScript ORM¶
一句话概述:Drizzle ORM 是一个超轻量(7.4kb)的 TypeScript ORM,零依赖、零代码生成,用类似 SQL 的语法写查询,天生适合 Serverless 和边缘计算环境。
核心知识点¶
| 概念 | 白话解释 |
|---|---|
| SQL-like 语法 | 查询写法跟 SQL 很像,会 SQL 就会 Drizzle |
| 零代码生成 | 不像 Prisma 需要 generate,定义完 Schema 直接用 |
| Drizzle Kit | CLI 工具,负责生成迁移文件和管理数据库 |
| Drizzle Studio | 可视化数据库浏览器 |
| 零依赖 | 没有额外依赖包,包体积极小 |
| 预编译语句 | prepare() 缓存 SQL 计划,高频查询性能更好 |
| Serverless Ready | 不需要二进制引擎,冷启动极快,适合 Cloudflare Workers 等 |
安装配置¶
PostgreSQL 项目¶
# 安装 Drizzle ORM 和 PostgreSQL 驱动
npm install drizzle-orm postgres # drizzle-orm: ORM 核心,postgres: PostgreSQL 驱动
npm install drizzle-kit --save-dev # drizzle-kit: 迁移和数据库管理 CLI
# 也可以用其他驱动
# npm install drizzle-orm pg # 用 node-postgres (pg) 驱动
# npm install drizzle-orm @neondatabase/serverless # 用 Neon Serverless
MySQL 项目¶
SQLite 项目¶
npm install drizzle-orm better-sqlite3 # better-sqlite3: SQLite 驱动
npm install drizzle-kit @types/better-sqlite3 --save-dev
配置文件¶
// drizzle.config.ts - Drizzle Kit 配置
import { defineConfig } from 'drizzle-kit' // 导入配置函数
export default defineConfig({
schema: './src/db/schema.ts', // Schema 文件路径
out: './drizzle', // 迁移文件输出目录
dialect: 'postgresql', // 数据库类型:postgresql/mysql/sqlite
dbCredentials: {
url: process.env.DATABASE_URL!, // 数据库连接地址
},
})
基本使用¶
定义 Schema¶
// src/db/schema.ts - 数据库表定义
import { pgTable, serial, text, integer, boolean, timestamp, varchar } from 'drizzle-orm/pg-core' // 导入 PostgreSQL 类型
import { relations } from 'drizzle-orm' // 导入关系定义
// 用户表
export const users = pgTable('users', { // 'users' 是数据库表名
id: serial('id').primaryKey(), // serial: 自增整数主键
name: text('name').notNull(), // text: 文本类型,不能为空
email: varchar('email', { length: 255 }).unique().notNull(), // varchar: 限长字符串,唯一
age: integer('age').default(0), // integer: 整数,默认值 0
isActive: boolean('is_active').default(true), // boolean: 布尔值
createdAt: timestamp('created_at').defaultNow(), // timestamp: 时间戳,默认当前时间
updatedAt: timestamp('updated_at').defaultNow(),
})
// 文章表
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content'),
published: boolean('published').default(false),
authorId: integer('author_id').references(() => users.id), // 外键,引用 users 表的 id
createdAt: timestamp('created_at').defaultNow(),
})
// 定义关系(用于关系查询 API)
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts), // 一个用户有多篇文章
}))
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { // 一篇文章属于一个用户
fields: [posts.authorId], // 本表的外键字段
references: [users.id], // 引用的目标字段
}),
}))
初始化连接¶
// src/db/index.ts - 数据库连接
import { drizzle } from 'drizzle-orm/postgres-js' // 导入 drizzle 初始化函数
import postgres from 'postgres' // 导入 postgres 驱动
import * as schema from './schema' // 导入所有 Schema 定义
const connectionString = process.env.DATABASE_URL! // 读取环境变量
const client = postgres(connectionString) // 创建数据库连接
export const db = drizzle(client, { schema }) // 创建 drizzle 实例,传入 Schema 以启用关系查询
CRUD 操作¶
import { db } from './db' // 导入数据库实例
import { users, posts } from './db/schema' // 导入表定义
import { eq, and, or, gt, like, desc, sql } from 'drizzle-orm' // 导入查询操作符
// ===== 创建(Insert) =====
const newUser = await db.insert(users).values({ // 插入一条记录
name: '张三',
email: 'zhang@example.com',
age: 25,
}).returning() // 返回插入的记录(PostgreSQL 支持)
// 批量插入
await db.insert(users).values([ // 一次插入多条
{ name: '李四', email: 'li@example.com' },
{ name: '王五', email: 'wang@example.com' },
])
// ===== 查询(Select) =====
// 查询所有
const allUsers = await db.select().from(users) // SELECT * FROM users
// 条件查询
const activeUsers = await db.select()
.from(users)
.where(
and( // 多条件 AND
eq(users.isActive, true), // is_active = true
gt(users.age, 18), // age > 18
)
)
.orderBy(desc(users.createdAt)) // 按创建时间倒序
.limit(10) // 只取 10 条
.offset(0) // 跳过 0 条(分页用)
// 只查部分字段
const names = await db.select({
id: users.id, // 只查 id
name: users.name, // 和 name
}).from(users)
// ===== 更新(Update) =====
await db.update(users)
.set({ name: '张三丰' }) // 要更新的字段
.where(eq(users.email, 'zhang@example.com')) // 条件
// ===== 删除(Delete) =====
await db.delete(users)
.where(eq(users.id, 1)) // 删除 id=1 的记录
关系查询¶
// 关系查询 API(类似 Prisma 的 include)
const usersWithPosts = await db.query.users.findMany({ // 用 query API
with: {
posts: true, // 包含关联的文章
},
where: eq(users.isActive, true), // 查询条件
limit: 10,
})
// 嵌套查询
const result = await db.query.users.findFirst({ // 查找第一条
with: {
posts: {
where: eq(posts.published, true), // 只查已发布的文章
limit: 5,
orderBy: desc(posts.createdAt),
},
},
})
高级用法¶
数据库迁移¶
# 根据 Schema 生成迁移文件
npx drizzle-kit generate # 对比 Schema 和数据库,生成 SQL 迁移
# 执行迁移
npx drizzle-kit migrate # 将迁移应用到数据库
# 直接推送(开发用,不生成迁移文件)
npx drizzle-kit push # 直接把 Schema 变更推送到数据库
# 查看数据库
npx drizzle-kit studio # 打开可视化界面(默认 https://local.drizzle.studio)
JOIN 查询¶
// 内连接(INNER JOIN)
const result = await db
.select({
userName: users.name, // 用户名
postTitle: posts.title, // 文章标题
})
.from(users)
.innerJoin(posts, eq(users.id, posts.authorId)) // users.id = posts.author_id
.where(eq(posts.published, true))
// 左连接(LEFT JOIN)
const usersWithPostCount = await db
.select({
name: users.name,
postCount: sql<number>`count(${posts.id})`.as('post_count'), // SQL 聚合函数
})
.from(users)
.leftJoin(posts, eq(users.id, posts.authorId)) // 左连接,没文章的用户也会显示
.groupBy(users.name) // 按用户名分组
事务¶
// 事务:要么全成功,要么全失败
await db.transaction(async (tx) => { // tx 是事务内的数据库实例
const [user] = await tx.insert(users).values({ // 在事务内创建用户
name: '赵六',
email: 'zhao@example.com',
}).returning()
await tx.insert(posts).values({ // 在事务内创建文章
title: '赵六的文章',
authorId: user.id,
})
// 如果任何操作失败,所有操作都会回滚
})
预编译语句(高性能)¶
// 高频查询用 prepare 预编译,避免重复解析 SQL
const findUserByEmail = db
.select()
.from(users)
.where(eq(users.email, sql.placeholder('email'))) // 用占位符
.prepare('find_user_by_email') // 命名并预编译
// 调用时传参
const user = await findUserByEmail.execute({ email: 'zhang@example.com' }) // 直接执行,省掉解析步骤
原生 SQL¶
// 需要复杂查询时可以写原生 SQL
import { sql } from 'drizzle-orm'
const result = await db.execute(sql`
SELECT name, count(*) as cnt
FROM ${users} -- 用模板字符串引用表,安全且有类型
GROUP BY name
HAVING count(*) > 1
`)
常见报错¶
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
relation "xxx" does not exist | 表不存在 | 运行 npx drizzle-kit push 同步数据库 |
Cannot find module 'drizzle-orm' | 没安装 | npm install drizzle-orm |
Schema is not defined | 没传 schema 到 drizzle() | 创建实例时加 { schema } |
| 关系查询报错 | 没定义 relations | 用 relations() 函数定义表关系 |
.returning() 报错 | MySQL/SQLite 不支持 | .returning() 只有 PostgreSQL 完整支持 |
| 迁移冲突 | 手动改了数据库结构 | npx drizzle-kit introspect 从数据库反向生成 Schema |
速查表¶
# Drizzle Kit CLI 命令
npx drizzle-kit generate # 生成迁移文件
npx drizzle-kit migrate # 执行迁移
npx drizzle-kit push # 直接推送 Schema(开发用)
npx drizzle-kit pull # 从数据库反向生成 Schema
npx drizzle-kit studio # 打开可视化管理界面
npx drizzle-kit check # 检查迁移一致性
# 常用查询操作符
eq(a, b) # a = b 等于
ne(a, b) # a != b 不等于
gt(a, b) # a > b 大于
gte(a, b) # a >= b 大于等于
lt(a, b) # a < b 小于
lte(a, b) # a <= b 小于等于
like(a, '%x%') # LIKE 模糊匹配
ilike(a, '%x%') # 不区分大小写的 LIKE
inArray(a, [1,2,3]) # IN (1,2,3)
isNull(a) # IS NULL
isNotNull(a) # IS NOT NULL
and(...) # AND 条件组合
or(...) # OR 条件组合
not(...) # NOT 取反
desc(a) # 降序排列
asc(a) # 升序排列
# PostgreSQL 字段类型
serial() # 自增整数
text() # 文本
varchar() # 限长字符串
integer() # 整数
boolean() # 布尔值
timestamp() # 时间戳
json() / jsonb() # JSON
uuid() # UUID
real() / doublePrecision() # 浮点数
参考:Drizzle ORM 官网 | Drizzle 入门 | GitHub