跳转至

Drizzle 轻量 TypeScript ORM

一句话概述:Drizzle ORM 是一个超轻量(7.4kb)的 TypeScript ORM,零依赖、零代码生成,用类似 SQL 的语法写查询,天生适合 Serverless 和边缘计算环境。

核心知识点

概念白话解释
SQL-like 语法查询写法跟 SQL 很像,会 SQL 就会 Drizzle
零代码生成不像 Prisma 需要 generate,定义完 Schema 直接用
Drizzle KitCLI 工具,负责生成迁移文件和管理数据库
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 项目

npm install drizzle-orm mysql2  # mysql2: MySQL 驱动
npm install drizzle-kit --save-dev

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 }
关系查询报错没定义 relationsrelations() 函数定义表关系
.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