Drizzle ORM Comprehensive Course – Sakura Dev
播放列表https://www.youtube.com/playlist?list=PLhnVDNT5zYN8PLdYddaU3jiZXeOyehhoU
总集数:10 集(2023-2024 年上传,适用于 2026 年 Drizzle 版本,核心概念兼容,但建议检查最新 docs 如 relations v2 更新)
整体风格:逐步构建从零到高级的 Drizzle ORM 项目,使用 PostgreSQL(部分集用 SQLite),代码密集型教学。配套 GitHub repo:https://github.com/vahid-nejad/drizzle-orm-course(包含完整代码示例,可 clone 跟练)。
核心重点:PostgreSQL 配置、schema 定义、查询过滤、关系建模(1:1、1:N、N:N)、NestJS 集成、数据插入与种子填充。强调类型安全、接近原生 SQL 的设计哲学。

1 – Setup - Schema - Migrations

这个视频演示了在 Node.js 应用(具体是 Next.js v13 项目,但适用于任何 Node.js 框架)中使用 PostgreSQL 初始设置 Drizzle ORM。重点是使用 Docker 配置 PostgreSQL 数据库、定义 schema、生成迁移并将其应用到数据库。

1. 使用 Docker 设置 PostgreSQL

2. 安装和配置 Drizzle ORM

3. 定义 Schema

4. 生成迁移

5. 将迁移推送到数据库

6. 一键迁移(生成 + 推送)

关键笔记

此过程为使用 Drizzle ORM 与 PostgreSQL 建立了坚实基础,在 Node.js 应用中启用类型安全的数据库交互。

2 – SQL Types

视频解释了 PostgreSQL 中的 SQL 数据类型及其在 Drizzle ORM 中的用法,强调它们在定义表字段的重要性。将类型分类为数字、布尔、文本、JSON、日期/时间、枚举和二进制,并提供详细解释和代码示例。

数字类型

浮点类型

布尔

文本类型

JSON 类型

日期/时间类型

枚举

数组

在 Drizzle ORM(PostgreSQL 驱动 drizzle-orm/pg-core)中,数组类型对象数组(即数组里每个元素是对象)的定义方式不同。PostgreSQL 原生支持数组,但不支持“数组里直接放复杂对象”,所以我们通常用两种方案来实现:

1. 简单类型数组(推荐:字符串、数字、UUID 等基本类型)

PostgreSQL 原生支持 text[]integer[]uuid[] 等,使用 .array() 方法即可。

常用附加配置(和普通列一样):

查询示例(使用数组操作符):

注意:PostgreSQL 数组允许元素为 null(如 {1, null, 3}),所以即使 .notNull(),类型推断仍可能是 (T | null)[]。可以用 .$type<T[]>() 强制。

2. 对象数组(推荐:用 jsonb 存储)

PostgreSQL 没有原生“对象数组”类型,但 jsonb 非常强大、性能优秀、查询方便,是存放对象数组的最佳实践(几乎所有现代项目都这么做)。

完整示例(带默认空数组 + 类型安全):

插入/更新示例

查询示例(强大!):

为什么 jsonb 是对象数组的最佳选择?

总结对比表(2026 年最常用方案):

需求推荐类型Drizzle 定义示例优点缺点
简单数组(标签、ID)text[] / integer[]text('tags').array()原生、查询快只能存基本类型
对象数组jsonbjsonb('variants').$type<T[]>()灵活、支持复杂嵌套、查询强大稍微多一点 overhead
超严格对象数组不用(不推荐)-PostgreSQL 无此类型

结论

需要完整 schema 示例、查询/插入代码、或 jsonb 索引优化?随时说!🚀

附加配置

视频在 test 表中使用这些类型演示,使用 Drizzle ORM 与 PostgreSQL,展示如何创建表、运行迁移,并在 Adminer 中验证结果。强调理解这些类型以有效使用 Drizzle ORM。

常用类型

在 Drizzle ORM 中定义 PostgreSQL 表时(使用 drizzle-orm/pg-core),最常用的数据类型如下表所示。这些是真实项目中出现频率最高的类型(2026 年最新推荐实践,优先使用 identity 而非旧 serial)。

类别函数名 (Drizzle)PostgreSQL 类型常见用途备注 / 推荐配置示例
数值 - 整数integer()integer / int / int4普通整数(如年龄、计数)integer('age')
 smallint()smallint / int2小范围整数(如状态码 0~100)smallint('status')
 bigint()bigint / int8大整数(如雪花ID、时间戳)bigint('bigId', { mode: 'number' })'bigint'
自增主键integer().generatedAlwaysAsIdentity()identity (推荐 2025+)现代标准主键(取代 serial)integer('id').primaryKey().generatedAlwaysAsIdentity()
 serial()serial / serial4传统自增主键(仍兼容,但官方推荐 identity)serial('id').primaryKey()
 bigserial()bigserial / serial8大范围自增主键bigserial('id', { mode: 'number' }).primaryKey()
精确小数numeric({ precision, scale })numeric / decimal金额、汇率等需要精确小数numeric('price', { precision: 12, scale: 2 })
浮点数real()real / float4科学计算、近似浮点real('temperature')
 doublePrecision()double precision / float8高精度浮点doublePrecision('score')
布尔boolean()boolean开关、是否已删除、激活状态boolean('isActive').default(false)
字符串text()text任意长度文本(如文章内容、描述)text('content')text('status', { enum: ['active', 'inactive'] })
 varchar({ length })varchar(n)有长度限制的字符串(如用户名、邮箱)varchar('email', { length: 255 }).unique()
JSONjsonb()jsonb结构化数据、配置、扩展字段(强烈推荐jsonb('metadata').$type<Record<string, any>>()
 json()json简单 JSON(不推荐,性能差)json('data')
时间日期timestamp({ withTimezone?, precision? })timestamp / timestamptz创建/更新时间(最常用timestamp('createdAt').defaultNow()
 date()date纯日期(如生日)date('birthday')
 time()time / timetz纯时间(如营业时间)time('openTime', { withTimezone: true })
UUIDuuid()uuid分布式唯一ID(非常流行uuid('id').primaryKey().defaultRandom()
枚举pgEnum('name', values) + enum()custom enum固定状态集合(如订单状态)const statusEnum = pgEnum('status', ['pending','paid']); statusEnum('status')
二进制bytea()bytea图片、文件等二进制数据bytea('avatar')
最实用推荐组合(生产级 schema 常用模式)

这些类型覆盖了 95%+ 的真实项目需求。
如果需要更小众的(如几何类型 pointarrayinterval 等),可以再告诉我,我继续补充!🚀

常用附加配置

在 Drizzle ORM 的 PostgreSQL 列定义(使用 drizzle-orm/pg-core)中,常用的附加配置(即链式方法)非常丰富。这些方法用于添加约束、默认值、索引暗示等,是日常开发中最常使用的。

以下是最常用、最实用的附加配置表格,按使用频率和重要性排序:

配置方法语法示例作用描述使用频率备注 / 常见组合示例
.notNull()varchar('email', { length: 255 }).notNull()强制列不允许 NULL 值(NOT NULL 约束)★★★★★最常用,几乎所有非可选字段都会加
.default(value)boolean('isActive').default(false)
timestamp('createdAt').defaultNow()
设置默认值(静态值或 defaultNow()★★★★★时间戳常用 defaultNow(),布尔常用 false/true
.defaultRandom()uuid('id').primaryKey().defaultRandom()生成随机 UUID(gen_random_uuid())★★★★现代项目 UUID 主键首选
.primaryKey()integer('id').primaryKey()
generatedAlwaysAsIdentity()
将列设为主键(PRIMARY KEY)★★★★★几乎所有表的主键都会加
.unique()varchar('email').unique()
unique('custom_name')
添加唯一约束(UNIQUE)★★★★邮箱、用户名、slug 等字段必加
.references(() => otherTable.column)integer('userId').references(() => users.id)定义外键引用(FOREIGN KEY),支持 onDelete/onUpdate★★★★关系表必备,可选加 .onDelete('cascade')
.generatedAlwaysAsIdentity({ ... })integer('id').primaryKey().generatedAlwaysAsIdentity()现代自增主键(identity,2025+ 推荐,取代 serial)★★★★生产级项目新标准,可指定 startWith/increment 等
$onUpdateFn(() => new Date())timestamp('updatedAt').$onUpdateFn(() => new Date())更新时自动刷新值(PostgreSQL trigger 方式)★★★updatedAt 字段常用,与 defaultNow() 搭配
.generatedAlwaysAs(sqlexpression)text('fullName').generatedAlwaysAs(sql{table.lastName})生成列(computed column),存储或虚拟★★少用,但适合派生字段(如 full_name = first + last)
.check(sqlcondition) (表级)(table) => [check('age_check', sql${table.age} > 18)]CHECK 约束(表级定义)★★年龄、状态等业务规则校验
.default(sqlexpression)integer('randomNum').default(sqlfloor(random() * 100)::int)使用 SQL 表达式作为默认值★★随机数、当前用户 ID 等
生产级常用模式示例(推荐组合)

这些配置基本覆盖了 98% 的真实场景。

需要更详细的某个配置示例(如外键的 onDelete 选项)或完整 schema 模板,再告诉我!🚀

3 – Write Your First Query

视频焦点是使用 Drizzle ORM 编写第一个查询。通过设置全局数据库客户端处理整个应用的 CRUD 操作,避免为每个查询创建新 Drizzle 实例。

 

要创建全局 DB 客户端,在 src/db 目录中创建新文件 index.ts。首先,使用 pg 库实例化 PostgreSQL 客户端。客户端使用从环境变量 (process.env.DATABASE_URL) 获取的连接字符串配置。初始化客户端后,调用 connect() 方法建立连接。

 

接下来,使用从 drizzle-orm/node-postgres 导入的 drizzle 函数创建并导出 Drizzle 客户端。此函数需要两个参数:PostgreSQL 客户端实例和 schema。从 schema.ts 文件导入 schema(别名为 schema)并作为第二个参数传递给 drizzle 函数。

 

设置全局 DB 客户端后,可以在应用任何地方导入并使用它进行数据库操作。例如,在 Next.js 页面(具体是异步服务器组件)中,从 /db 导入 DB 客户端。编写一个简单查询从 users 表获取所有用户:db.select().from(users),其中 db 指导出的 Drizzle 客户端,schema.users 引用 schema 中定义的 users 表。然后使用 JSON.stringify(results) 在页面上渲染结果。

 

要测试查询,必须运行 PostgreSQL Docker 容器(通过 Docker Compose)。执行时,查询从 users 表检索所有记录。在示例中,数据库中有两个虚拟用户 ("John Doe" 和 "Steve Doe"),查询成功返回两个记录。

 

这演示了使用 Drizzle ORM 编写简单 select 查询的基本语法,强调使用全局 DB 客户端和适当 schema 集成以实现类型安全和易用性。

4 – All Query Filters

视频涵盖 Drizzle ORM 中的所有查询过滤器,演示如何在包含 idfullNamephoneaddressscore 列的 users 表上应用各种条件。解释包括每个过滤器类型的关键概念、代码示例和逐步过程。

设置和初始查询

平等和比较过滤器

Null 检查

IN 和 NOT IN

范围过滤器

字符串模式匹配

逻辑运算符

实施笔记

这涵盖了视频中演示的 Drizzle ORM 中的所有查询过滤器。

5 – One To One Relation

视频解释了使用 Drizzle ORM 在两个表——usersprofiles——之间创建一对一关系的过程。关键概念是每个用户可以有一个精确的配置文件,每个配置文件属于一个用户,建立双向一对一关系。

1. 在 Schema 中定义表

在现有 users 表旁边创建 profiles 表。使用 Drizzle ORM 的 pgTable 函数定义 schema。

注意:外键 (userId) 放置在 profiles 表中,引用 users.id 主键。这是一个设计选择——任何一方都可以持有外键,但需要一致性。

2. 运行迁移

定义 schema 后,执行迁移在数据库中创建 profiles 表:

这生成 profiles 表,包含列:idbiouser_id(带有到 users.id 的外键约束)。

3. 在 Drizzle ORM 中定义关系

为了启用带有相关数据的类型安全查询,必须使用 Drizzle ORM 的 relations 函数显式定义关系。

这告诉 Drizzle ORM,一个 user 可以有一个关联的 profile,通过 profiles 表中的 userId 外键链接。

4. 使用 with 查询相关数据

要获取用户及其配置文件,使用查询构建器中的 with 选项。

5. 结果结构

返回的对象包括嵌套相关数据:

这演示了一对一关系成功建立和查询。

关键点

此设置通过数据库约束确保数据完整性,并在使用 Drizzle ORM 的应用中启用高效、类型安全的检索。

6 – One To Many Relation

视频解释了使用 Drizzle ORM 在两个表——具体是 users 表和 posts 表——之间创建一对多关系的过程。关系定义为一个用户可以有多个帖子,而每个帖子属于一个用户。

1. 定义 posts 表 Schema

表包含以下字段:

示例代码:

2. 在 users 表中建立关系

users 表的 relations 回调中,定义 many 关系将用户连接到其帖子。这表明一个用户可以有零个或多个帖子。

示例代码:

在这里,many(posts)usersposts 建立一对多链接。

3. 在 posts 表中定义反向关系

posts 表还需要关系定义,以表明每个帖子属于一个用户(作者)。这使用 one 函数完成。

示例代码:

4. 创建并应用迁移

定义 schema 和关系后:

为了强制外键,在迁移中更新 authorId 字段包括:

这确保 posts.authorIdusers.id 之间的引用完整性。

5. 种子测试数据

将虚拟数据插入数据库以测试关系:

6. 使用关系查询

两种类型的查询演示关系如何工作:

a. 获取用户及其帖子
使用带有 with 子句的 db.query.users.findFirst 包括相关帖子:

结果:返回用户及其 posts 数组,包含所有 authorId 匹配用户 ID 的帖子。

b. 获取帖子及其作者
使用 db.query.posts.findFirst 获取帖子并包括其作者:

结果:返回帖子以及嵌套的 author 对象,包含完整用户数据(例如 ID、名称等)。

关键概念解释

此设置允许在两个方向高效查询相关数据,同时保持类型安全和数据库完整性。

7 – Many To Many Relations

视频解释了使用实际示例在 posts 表和 categories 表之间实现许多对许多关系。每个帖子可以属于多个类别,每个类别可以与多个帖子关联。这通过创建连接表并定义主表和连接表之间的一对多关系实现。

场景和数据库设计

在关系数据库中,不支持直接许多对许多关系。相反,创建 连接表(例如 post_categories)链接两个表。这将许多对许多关系分解为两个一对多关系:

在 Drizzle ORM 中的逐步实现

  1. schema.ts 中定义表

    • Categories 表

    • 连接表 (post_categories)

      • postId 是引用 posts.id 的外键。
      • categoryId 是引用 categories.id 的外键。
    • 连接表的主键 连接表使用组合主键结合 postIdcategoryId

  2. 定义关系

    • Post 关系

      • 一个帖子在 post_categories 连接表中有许多记录。
    • Category 关系

      • 一个类别在 post_categories 连接表中有许多记录。
    • 连接表关系

      • 连接表与 postscategories 都有许多对一关系。
  3. 使用关系查询

    • 获取帖子及其类别

      • 这检索帖子及其相关 post_categories 记录。

      • 要包括类别细节(例如名称),使用:

        • 结果包括类别名称(例如 "Sport"、"Economics")。
    • 获取类别及其帖子

      • 这检索类别及其通过连接表的相关帖子。
    • 自定义选择字段

      • 要从连接表排除 postIdcategoryId

  4. 迁移和测试

    • 运行迁移创建表:

      • 这创建 categoriespost_categoriesposts 表,带有适当外键。
    • 数据手动插入(未来集覆盖种子)。

    • 使用数据库客户端(例如 Adminer)验证:

      • categories 表有如 "Sport"、"Economics" 的条目。
      • posts 表有 ID 1 和 2 的帖子。
      • post_categories 表链接帖子到类别(例如 Post 1 有 Category 1 和 2)。
  5. API 路由示例

    • 在 API 路由(例如 /api/user)中,查询数据库:

      • 返回帖子及其作者和分类数据(例如类别名称)。

关键概念

此实现允许灵活查询和数据检索,同时通过外键维护引用完整性。未来集将覆盖插入表中各种关系类型的数据(一对一、一对多、许多对许多)。

8 – Drizzle ORM in NestJS

视频演示了通过替换 Prisma 将 Drizzle ORM 与 NestJS 项目集成。过程涉及:

  1. 移除 Prisma:从 package.json 删除 prisma(依赖和 devDependencies),并移除 prisma.service.ts 文件。

  2. 安装依赖:通过 npm 安装 drizzle-ormbetter-sqlite3(SQLite 驱动)和 drizzle-kit(dev 依赖)。

  3. 创建 Drizzle 模块:使用 nest g m drizzle 生成 drizzle 模块。移除现有 prisma.service.ts

  4. 定义 Schema:在 drizzle 目录中创建 schema.ts。使用 Drizzle 的 SQLite 表函数定义 users 表:

    使用从 drizzle-orm/sqlite-coreintegertext,不是 pg-core

  5. 创建 Drizzle Provider:在 drizzle.provider.ts 中,使用 NestJS 的工厂模式定义异步 provider:

    better-sqlite3 导入 Database(不是 sqlite3)以避免运行时错误。

  6. 注册 Provider:在 drizzle.module.ts 中,在 providers 数组中展开 drizzleProvider

  7. 在服务中注入 DB:在 user.service.ts 中,使用 @Inject 注入 Drizzle DB 客户端:

  8. 重构 CRUD 操作

    • 创建:使用 db.select().from(users).where(eq(users.email, dto.email)) 检查重复电子邮件。插入使用:

    • 按电子邮件/ID 查找:使用 db.select().from(users).where(eq(users.email, email))eq(users.id, id)

  9. 在用户模块中注册:在 user.module.ts 中用 drizzleProvider 替换 Prisma provider。

  10. 运行迁移

    • 创建 drizzle.config.ts

    • 运行 npx drizzle-kit generate:sqlite 创建迁移。

    • 运行 npx drizzle-kit push:sqlite --db-path ./sqlite.db 应用它(注意:config 中 env 变量可能不加载;临时硬编码路径)。

  11. 测试:使用 Insomnia 测试 /auth/register/auth/login。应用创建用户、检查重复并返回令牌。

关键笔记

9 – Inserting Data

视频解释了使用 Drizzle ORM 将新数据插入 PostgreSQL 数据库,焦点是处理表之间关系:一对一、一对多和许多对许多。schema 包括 userspostscategoriesprofiles 表,带有以下关系:

1. 插入新用户(无关系)

逐步过程:

代码示例:

2. 一对一插入(用户 + 配置文件)

逐步过程:

代码示例:

3. 一对多插入(用户 + 多个帖子)

逐步过程:

代码示例:

4. 许多对许多插入(用户 + 帖子 + 类别)

逐步过程:

代码示例:

关键要点

此方法确保跨相关表的数据完整性,同时利用 Drizzle ORM 的类型安全查询能力。

10 – Seeding

什么是种子及其用途

种子指的是为测试目的自动将虚拟数据插入数据库的过程。这消除了每次测试时手动输入数据的需要。通过使用种子文件,开发者可以通过终端中的单个命令用一致的测试数据初始化数据库。

为 Drizzle ORM Schema 创建 Schema 图

在实现种子之前,视频演示了如何创建 Drizzle ORM schema 的视觉图以理解结构和表之间关系。schema 包括:

图使用其关系视觉连接这些表:

此结构确保 PostgreSQL 中的引用完整性和适当数据建模。

使用 Drizzle ORM 实现种子功能

要实现种子,在 db 目录中创建 seed.ts 文件。过程涉及:

  1. 设置数据库连接

    • 因为种子文件独立于主应用运行,需要单独数据库连接。

    • 使用 pg 库创建 PostgreSQL 连接池:

    • 使用此池初始化 drizzle 客户端:

    • 使用 dotenv/config 访问环境变量:

  2. 创建主种子函数

    • 定义 async main() 函数处理数据库操作:

    • 调用函数并处理错误:

  3. 插入虚拟数据

    • Users 和 Profiles

      • for 循环插入 10 个用户:

      • 每个用户获取相应配置文件,通过 userId 链接。

    • Categories

      • 批量插入两个类别:

    • Posts

      • 插入两个帖子,链接到最后一个插入的用户:

    • Pivot 表 (PostsOnCategories)

      • 填充连接表链接帖子和类别:

使用 Faker.js 用于真实数据

为了避免硬编码重复数据,使用 Faker.js 库生成随机、真实值:

示例:

这确保每个种子记录有独特、可信数据。

运行种子功能

  1. package.json 中添加脚本:

  2. 运行种子命令:

  3. 终端输出:

  4. Drizzle Studio(GUI 工具)显示填充的表带有虚拟数据。

关键要点

到最后,开发者可以使用单个命令用真实、相关数据种子 Drizzle ORM 数据库。

系列总结建议

这个系列是 YouTube 上最完整的 Drizzle 实战课之一,跟着 repo 一边敲代码一边看,学完能直接上手项目!
如果想针对某集要更详细的代码示例或 2026 年更新适配,告诉我~ 🚀