Drizzle 最适合用一句话理解:
它不是帮你“忘掉 SQL”的 ORM,而是给你一套带比例尺、坐标系、图例、道路编号的数据库地图系统。你仍然在地图上行走,但不会再闭眼走路。
如果把数据库开发想成城市建设,那么:
数据库表 = 城市地块
schema = 城市总平面图
migration = 施工变更单
query builder = 规划路线的尺子和图钉
relations = 地图上的道路连通关系
raw SQL = 直接看经纬度自己开车Drizzle 的价值,不在“替你做决定”,而在“让你每一步都看得见,而且类型系统跟着你走”。
#一、先把 Drizzle 放到正确坐标系里
很多人第一次看 Drizzle,会问一句:
“它到底是 ORM,还是 SQL builder?”
正确答案是:它两边都占,但重心明显偏向“类型化 SQL builder”。
可以把常见方案排成一条线:
纯 SQL <------ Drizzle ------> 重抽象 ORM
控制力高 自动化高
魔法少 魔法多
SQL感强 框架感强再具体一点:
Raw SQL
你自己写 SQL、自己维护类型、自己做结果映射
Drizzle
你用 TypeScript 声明 schema,用接近 SQL 的方式写查询,
类型跟着 schema 和 select 结果走
Prisma
你先写 Prisma 自己的 schema DSL,再生成 Prisma Client,
查询体验更像“操作模型对象”
TypeORM
你先写实体类和装饰器,依赖运行时元数据,
查询可以走 repository,也可以转去 QueryBuilder所以 Drizzle 的核心心智不是“对象世界替代 SQL 世界”,而是:
SQL 仍然是世界本体
TypeScript 负责把 SQL 世界精确投影到代码里
Drizzle 就是那台投影仪这也是为什么官方一直强调它是贴近 SQL 的、薄的一层 typed layer。
#二、Drizzle 的真正心智:不是“会不会 ORM”,而是“会不会画图”
理解 Drizzle,最重要的是这张图:
┌──────────────────────┐
│ TypeScript Schema │
│ 表/列/约束/索引定义 │
└──────────┬───────────┘
│
推导类型 │ 生成迁移/对比差异
│
┌────────────────┴──────────────┐
│ │
┌─────────▼─────────┐ ┌─────────▼─────────┐
│ Query Builder │ │ Drizzle Kit │
│ select/insert/... │ │ generate/push/... │
└─────────┬─────────┘ └─────────┬─────────┘
│ │
└──────────────┬────────────────┘
│
┌───────▼───────┐
│ SQL 数据库 │
└───────────────┘这张图说明了 4 个关键事实:
- Drizzle 不是把数据库藏起来,而是把数据库结构显式写进 TypeScript。
- 类型不是凭空来的,而是从 schema 和 query shape 推导来的。
- migration 不是黑盒魔法,而是 schema 变化的 SQL 落地过程。
- 你随时可以退回 SQL,不会被框架关进笼子里。
所以,Drizzle 的学习重点不是“记住多少 API”,而是建立这套判断:
- 结构定义写在 schema。
- 结构演进走 migration。
- 常规查询走 SQL-like builder。
- 关系嵌套走 relations/query API。
- builder 表达不了的地方,用
sql``补进去。
这套分工一旦清楚,Drizzle 就会非常顺手。
#三、Schema-first 与 TS-first,到底是什么意思
很多人会把这两个词混着用,但在 Drizzle 里最好分开理解。
#1. Schema-first:先想结构,不先想对象
在 Drizzle 里,数据库设计的起点是表、列、主键、外键、索引、约束。
不是先问:
“我有一个 User 类,它有哪些方法?”
而是先问:
“数据库里到底有哪些表?列是什么?关系如何落地?”
这是非常 SQL 的思维。
#2. TS-first:这份结构图写在 TypeScript 里
Drizzle 的特别之处在于,它没有再发明一套独立 DSL 来写 schema,而是直接让你在 TypeScript 里声明数据库结构。
也就是:
Schema-first:先定义数据库结构
TS-first:这份结构定义直接写在 TypeScript 中所以 Drizzle 本质上是:
Schema-first inside TypeScript
在 TypeScript 里做 schema-first这和 Prisma 很不一样。
- Prisma:
schema.prisma是独立 DSL,之后生成 Client - Drizzle:
schema.ts本身就是源码的一部分,直接参与类型推导和迁移流程
#四、先看一个完整 schema:你会立刻明白 Drizzle 的气质
下面用 PostgreSQL 举例。
// src/db/schema.ts
import {
pgTable,
serial,
text,
integer,
timestamp,
primaryKey,
} from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
createdAt: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
});
export const posts = pgTable("posts", {
id: serial("id").primaryKey(),
authorId: integer("author_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
title: text("title").notNull(),
body: text("body").notNull(),
createdAt: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
});
export const comments = pgTable("comments", {
id: serial("id").primaryKey(),
postId: integer("post_id")
.notNull()
.references(() => posts.id, { onDelete: "cascade" }),
authorId: integer("author_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
content: text("content").notNull(),
createdAt: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
});
export const tags = pgTable("tags", {
id: serial("id").primaryKey(),
name: text("name").notNull().unique(),
});
export const postsToTags = pgTable(
"posts_to_tags",
{
postId: integer("post_id")
.notNull()
.references(() => posts.id, { onDelete: "cascade" }),
tagId: integer("tag_id")
.notNull()
.references(() => tags.id, { onDelete: "cascade" }),
},
(t) => [primaryKey({ columns: [t.postId, t.tagId] })]
);
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
comments: many(comments),
}));
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
comments: many(comments),
postsToTags: many(postsToTags),
}));
export const commentsRelations = relations(comments, ({ one }) => ({
author: one(users, {
fields: [comments.authorId],
references: [users.id],
}),
post: one(posts, {
fields: [comments.postId],
references: [posts.id],
}),
}));
export const tagsRelations = relations(tags, ({ many }) => ({
postsToTags: many(postsToTags),
}));
export const postsToTagsRelations = relations(postsToTags, ({ one }) => ({
post: one(posts, {
fields: [postsToTags.postId],
references: [posts.id],
}),
tag: one(tags, {
fields: [postsToTags.tagId],
references: [tags.id],
}),
}));这段代码很能说明 Drizzle 的风格:
- 表结构是显式的。
- 外键是显式的。
- 多对多不会帮你偷偷建中间表,必须自己定义。
- relation 不是神秘黑盒,而是你明确告诉框架“路怎么连”。
这就是“地图派”风格。
#五、Schema 文件怎么组织,才像工程而不是样例
官方允许你把 schema 放一个文件,也允许拆成多个文件。真正工程里,常见做法是按领域拆分:
src/
db/
schema/
users.ts
posts.ts
comments.ts
tags.ts
index.ts关键原则只有一个:
Drizzle Kit 需要能拿到所有导出的模型。
也就是说,你拆文件没问题,但要保证表、枚举、视图、关系等都被正确导出,否则 migration diff 会不完整。
如果你想在代码里用驼峰、数据库里用蛇形,也可以在初始化时做 casing 映射:
// src/db/client.ts
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import * as schema from "./schema";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
export const db = drizzle(pool, {
schema,
casing: "snake_case",
});这个细节很实用,因为它能让你少写很多手动列别名。
#六、Migration:Drizzle 不是只有一条路,而是三条路
Drizzle Kit 最值得讲透的地方,不是命令本身,而是它允许三种迁移哲学并存。
1. generate + migrate
TS schema --> SQL migration files --> apply to DB
2. push
TS schema --> diff --> direct apply to DB
3. pull
existing DB --> introspect --> schema.ts#1. generate + migrate:最像正统工程流
// drizzle.config.ts
import "dotenv/config";
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/db/schema.ts",
out: "./drizzle",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});npx drizzle-kit generate
npx drizzle-kit migrate这条路的心智是:
- TypeScript schema 是源码里的地图底稿
generate负责产出 SQL 变更单migrate负责把变更单应用到数据库
这通常最适合团队协作,因为 SQL 文件会进入版本库,变更历史清晰。
#2. push:更像快速施工
npx drizzle-kit pushpush 的机制是:
- 读你的 schema
- 反查数据库当前结构
- 做 diff
- 直接应用 SQL
它非常适合原型、个人项目、早期开发。
工程上的判断是:
- 开发期、试验期:
push很爽 - 多人协作、审计要求高、生产控制严格:通常更偏向
generate + migrate
这句是工程判断,但非常重要,因为 migration 从来不只是“能不能改表”,而是“变更历史是否可追踪”。
#3. pull:接管已有数据库
如果你不是从零开始,而是接一个老库,Drizzle 也支持 database-first:
npx drizzle-kit pull它会把数据库现状拉回成 schema.ts,让你的 TypeScript 世界和数据库世界对齐。
这点很关键,因为很多 ORM 一上来就假设“必须从我开始建库”,而 Drizzle 明确支持两种现实:
- codebase-first
- database-first
换句话说,它承认真实世界不总是新项目。
#4. 自定义迁移:别把工具能力当成边界
如果某些 DDL 变更或数据修复不适合自动生成,Drizzle Kit 支持自定义空迁移文件:
npx drizzle-kit generate --custom --name=seed-users这说明 Drizzle 的设计立场很清楚:
“自动化是默认选项,但最终控制权留给你。”
#七、查询:Drizzle 最像 SQL 的地方,恰恰是它最强的地方
很多 ORM 的问题是:简单查询很顺手,一复杂就要么类型丢失,要么被迫写字符串 SQL。
Drizzle 的思路是:
- 常规查询,用 SQL-like builder
- 关系嵌套,用 query/relations API
- 特殊表达式,用
sql``
这三层拼起来,覆盖面非常完整。
#八、先掌握 SQL-like 查询:把它当“带类型的 SQL”
#1. Insert
const createdUser = await db
.insert(users)
.values({
name: "Lin",
email: "lin@example.com",
})
.returning();你会发现它几乎就是 SQL 的顺序:
insert into users ... values ... returning ...#2. Select
import { eq, desc } from "drizzle-orm";
const latestPosts = await db
.select({
id: posts.id,
title: posts.title,
authorName: users.name,
createdAt: posts.createdAt,
})
.from(posts)
.leftJoin(users, eq(posts.authorId, users.id))
.orderBy(desc(posts.createdAt))
.limit(10);注意这里一个非常关键的 Drizzle 特征:
select 里写出来的 shape,基本就是结果类型的 shape。
也就是说,你选什么,类型就长什么样。
#3. Update
const updated = await db
.update(posts)
.set({ title: "Drizzle ORM 深入理解" })
.where(eq(posts.id, 1))
.returning();#4. Delete
const deleted = await db
.delete(comments)
.where(eq(comments.id, 9))
.returning();这套 API 的好处,是 SQL 心智完全不需要重建。
你不是在学习一种“框架专属查询语言”,你是在写被 TypeScript 扶住的 SQL。
#九、聚合、表达式、原生能力补位:sql`` 是 Drizzle 的逃生舱,也是增强器
真实项目里最怕一种 ORM:80% 能写,剩下 20% 只能逃回字符串 SQL,而且类型和参数化一起丢掉。
Drizzle 的 sql`` 恰好就是为这个问题准备的。
import { sql } from "drizzle-orm";
const stats = await db
.select({
authorId: posts.authorId,
postCount: sql<number>`count(${posts.id})`.mapWith(Number),
})
.from(posts)
.groupBy(posts.authorId);或者直接执行原生 SQL:
const userId = 42;
const result = await db.execute(
sql`select * from ${users} where ${users.id} = ${userId}`
);这里有两个重点:
- 表名、列名会被安全映射。
- 动态值会走参数化,不是字符串拼接。
所以 sql`` 不是“放弃类型”,而是“在保留参数化和部分类型辅助的前提下,补足 SQL 表达力”。
这也是 Drizzle 和“纯 ORM 封装派”最大的不同之一。
#十、Relations:它不是数据库魔法,而是应用层的连线图
这是 Drizzle 最容易被误解的地方。
官方文档里专门强调了一点:
relations 和 foreign keys 不是一回事。
可以这样记:
foreign key = 数据库层的硬约束
relations = 应用层的导航关系更像地图的话:
foreign key = 真实存在的道路护栏
relations = 导航软件里的可通行路线两者经常一起出现,但不是同一个层面。
#1. 一对多
users (1) --------< posts (n)export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));#2. 多对多
posts (n) ----< posts_to_tags >---- tags (n)多对多在 Drizzle 里必须显式经过中间表。这个设计很“数据库本位”,但工程上反而更稳,因为中间表往往迟早会长出额外字段,比如:
- 排序
- 权重
- 创建时间
- 创建人
也就是说,Drizzle 不是把多对多“藏起来”,而是提醒你:数据库里它本来就不是空气。
#十一、关系查询:什么时候该用 db.query.*
当你只是要一张扁平结果表,优先用 core builder。
当你要嵌套结构,比如“文章带作者,评论再带评论作者”,就很适合 relations 查询。
const feed = await db.query.posts.findMany({
with: {
author: true,
comments: {
with: {
author: true,
},
},
},
});它返回的就是嵌套对象结构,不需要你手工把 join 结果再做一次 regroup。
这层 API 的意义不是“替代 SQL-like 查询”,而是:
“当目标结果本来就是嵌套 JSON 结构时,别再手工拼装了。”
所以一个很实用的判断标准是:
- 要扁平表结果、聚合、复杂 join:core builder
- 要嵌套对象树:relations/query API
二者不是竞争关系,是分工关系。
#十二、一个完整例子:列表、详情、统计,各用哪种写法
#1. 列表页:扁平化查询优先
import { desc, eq } from "drizzle-orm";
const list = await db
.select({
postId: posts.id,
title: posts.title,
authorName: users.name,
createdAt: posts.createdAt,
})
.from(posts)
.innerJoin(users, eq(posts.authorId, users.id))
.orderBy(desc(posts.createdAt))
.limit(20);这是典型“SQL builder 擅长场景”。
#2. 详情页:嵌套结构优先
const detail = await db.query.posts.findFirst({
with: {
author: true,
comments: {
with: {
author: true,
},
},
},
});这是典型“relations 擅长场景”。
#3. 统计页:聚合优先,回到 core builder
import { sql } from "drizzle-orm";
const topAuthors = await db
.select({
authorId: users.id,
authorName: users.name,
postCount: sql<number>`count(${posts.id})`.mapWith(Number),
})
.from(users)
.leftJoin(posts, eq(posts.authorId, users.id))
.groupBy(users.id, users.name)
.orderBy(sql`count(${posts.id}) desc`);这就是最典型的 Drizzle 使用方式:
列表 -> SQL-like builder
详情 -> relations query
统计 -> builder + sql不是“全都用一种写法”,而是“按目标结果选武器”。
#十三、动态查询:Drizzle 为什么默认不让你重复 .where()
Drizzle 有一个很有意思的设计:
默认情况下,很多 query method 只能调用一次。
比如:
const query = db
.select()
.from(users)
.where(eq(users.id, 1))
.where(eq(users.name, "Lin")); // 类型报错这不是故意为难你,而是因为 SQL 语义里本来就只有一个 WHERE 子句。Drizzle 宁可在类型层面阻止歧义,也不帮你偷偷“猜测并合并”。
如果你确实要动态拼查询,可以进入动态模式:
function withPagination<T extends typeof baseQuery>(qb: T, page = 1, pageSize = 10) {
return qb.limit(pageSize).offset((page - 1) * pageSize);
}
const baseQuery = db.select().from(users).$dynamic();
const pagedQuery = withPagination(baseQuery, 2, 20);这背后体现的还是同一个哲学:
“默认尊重 SQL 本体,动态拼装时再明确切换模式。”
#十四、和 Prisma、TypeORM、SQL 直写,到底怎么比
#1. 总览表
| 方案 | 结构定义方式 | 查询体验 | 类型来源 | 迁移心智 | 代价 |
|---|---|---|---|---|---|
| Drizzle | schema.ts |
贴近 SQL,另有关系查询 | schema + select shape | generate / migrate / push / pull 多路线 |
需要懂 SQL 和数据库设计 |
| Prisma | schema.prisma |
更像操作模型客户端 | 生成的 Prisma Client | 围绕 Prisma schema 与 migrate | 抽象更高,离 SQL 更远 |
| TypeORM | 实体类 + 装饰器 | repository + QueryBuilder | 运行时元数据 + TS | 围绕 entity/migration | 容易在复杂查询时切到另一套心智 |
| Raw SQL | .sql 或字符串 |
直接 SQL | 你自己维护 | 你自己维护 | 控制最大,重复劳动也最大 |
#2. Drizzle vs Prisma
Prisma 的强项是:
- 上手体验统一
include、嵌套写法直观- Client 生成后类型体验很完整
Drizzle 相比 Prisma 的差异,不是“类型更强”这么简单,而是“离 SQL 更近”。
可以这样记:
Prisma:先写 Prisma 世界,再生成访问数据库的客户端
Drizzle:直接在 TypeScript 里画数据库地图,然后沿着 SQL 走所以如果你的团队希望:
- 开发者尽量少碰 SQL
- 更喜欢模型式客户端
- 接受独立 schema DSL
那 Prisma 会更顺手。
如果你的团队希望:
- SQL 就是主语言
- 类型必须跟 SQL 结果形状紧贴
- 不想被框架 DSL 和生成物强绑定
Drizzle 会更合拍。
#3. Drizzle vs TypeORM
TypeORM 的典型心智是实体类、装饰器、关系装饰、repository、query builder。
它很像在说:
“先把数据库世界翻译成面向对象实体。”
Drizzle 则像在说:
“别急着翻译,先把数据库世界准确画出来。”
两者最大的差异点在这里:
- TypeORM 更偏 entity-first、metadata-first
- Drizzle 更偏 schema-first、SQL-first、type-first
复杂查询时,这种差异会尤其明显。
TypeORM 官方文档自己也区分了:
- 简单查找用
find* - 更复杂的查询转
QueryBuilder
而 Drizzle 从一开始就把“SQL-like builder 是主战场”这件事说得很明白,所以心智更统一。
#4. Drizzle vs SQL 直写
这组对比最容易误判。
很多人以为:
“我都懂 SQL 了,为什么不直接写 SQL?”
答案是:当然可以,Drizzle 也不阻止你。
但直写 SQL 的长期成本在于:
- 查询结果类型要自己维护
- 表名列名重构时缺少编译期保护
- join 后结果 shape 容易和调用方脱节
- 复用与组合能力弱
Drizzle 不是替代 SQL,而是把 SQL 工程化。
所以更准确的说法不是:
“Drizzle 比 SQL 更强。”
而是:
“Drizzle 让 SQL 在 TypeScript 项目里更可维护。”
#十五、最常见的 6 个误区
#误区 1:Drizzle 就是 Prisma 的轻量替代
不是。
Prisma 的核心体验是“生成客户端操作模型”,Drizzle 的核心体验是“用 TypeScript 写 schema,用接近 SQL 的方式查询”。
它们不是同一条设计路线的轻重版本。
#误区 2:定义了 relations 就等于建了外键
不是。
relations 是应用层关系描述,不会自动替你创建数据库外键。外键是否存在,取决于你的表定义和 migration。
#误区 3:用了 Drizzle,就可以不懂 SQL
恰恰相反。
Drizzle 的上限很高,前提是你接受 SQL 是主语。越懂 SQL,越能发挥 Drizzle。
#误区 4:push 一定比 migration 文件更先进
不是。
push 是快速,但不总是更适合团队工程。涉及审计、回滚、评审、环境一致性时,SQL migration 文件通常更稳。
#误区 5:多对多关系应该自动隐藏中间表
不一定。
在真实业务里,中间表经常会长业务字段。Drizzle 让你显式建中间表,反而更贴近长期演化。
#误区 6:聚合结果天然就是 number
不一定。
官方文档明确提醒,某些数据库里 count() 之类的结果可能是 bigint 或 decimal 语义,需要用 .mapWith(Number) 或相关 API 处理。
#十六、Drizzle 适合什么项目
- TypeScript 后端项目,尤其是 Node/Bun/Deno/Edge 场景。
- 团队已经接受 SQL 是核心能力,而不是希望完全躲开 SQL。
- 你需要精确控制 schema、索引、外键、join、聚合、原生表达式。
- 你要接管已有数据库,而不是只能从新库开始。
- 你希望 schema、类型、查询、迁移在同一套源码里收敛。
更具体地说,下面这类团队通常会很喜欢 Drizzle:
- 后端工程师本来就会写 SQL
- 代码评审重视 migration 可读性
- 讨厌过多运行时魔法
- 希望 query shape 和 TS 类型一一对应
#十七、Drizzle 不太适合什么项目
- 团队明确不想碰 SQL,只想“像操作对象一样操作数据库”。
- 项目成员对数据库设计基础较弱,希望框架尽量兜底。
- 你特别依赖重 ORM 的实体生命周期、深度级联、统一 repository 风格。
- 项目不以 TypeScript 为中心。
- 你的核心目标不是“更强控制力”,而是“更强默认抽象”。
一句话概括就是:
想让框架替你想很多,Drizzle 可能不够“厚”
想自己想清楚并且要类型系统全程跟随,Drizzle 很合适#十八、实战选择建议:到底什么时候该选 Drizzle
如果你现在正在做技术选型,可以直接用这张决策图:
团队是否接受 SQL 是一等公民?
否 -> 更偏 Prisma / 更高抽象方案
是 -> 继续
是否希望 schema 直接写在 TypeScript 中?
否 -> 更偏 Prisma DSL / 传统 SQL migration 流
是 -> 继续
是否希望复杂查询依然保留编译期类型约束?
否 -> Raw SQL 也可以
是 -> Drizzle 很有竞争力再说得更实用一点:
- 你是“数据库地图绘制派”而不是“对象自动驾驶派”时,选 Drizzle。
- 你想要的是“精确的尺子”,不是“热心但偶尔猜错的管家”时,选 Drizzle。
- 你愿意理解数据库本体,并希望 TypeScript 成为数据库设计的放大镜时,选 Drizzle。
#十九、总结
Drizzle 最值得学的,不是某个 API 名字,而是这套稳定心智:
数据库不是对象仓库,而是结构化地图
schema 是地图底稿
migration 是地图改版记录
query builder 是有类型的量尺
relations 是导航连线
sql`` 是保底也是增强所以 Drizzle 的本质,不是“帮你忘掉 SQL”,而是“帮你把 SQL、类型系统、迁移历史,画进同一张工程地图”。
你一旦接受这件事,它就会非常顺。
你一旦期待它像重 ORM 那样替你藏起数据库,它反而会显得“不够热情”。
这不是缺点,这是它的设计立场。
而这也正是 Drizzle 最强的地方:
它让数据库开发回到本体,同时把 TypeScript 的编译期能力,真正接到了数据库这张地图上。
#官方来源链接清单
- Drizzle ORM Overview: https://orm.drizzle.team/docs/overview
- Drizzle Schema: https://orm.drizzle.team/docs/sql-schema-declaration
- Drizzle Query Data: https://orm.drizzle.team/docs/data-querying
- Drizzle Select: https://orm.drizzle.team/docs/select
- Drizzle
sqlOperator: https://orm.drizzle.team/docs/sql - Drizzle Relations Fundamentals: https://orm.drizzle.team/docs/relations-schema-declaration
- Drizzle Relations: https://orm.drizzle.team/docs/relations
- Drizzle Migrations Fundamentals: https://orm.drizzle.team/docs/migrations
- Drizzle Kit
generate: https://orm.drizzle.team/docs/drizzle-kit-generate - Drizzle Kit
migrate: https://orm.drizzle.team/docs/drizzle-kit-migrate - Drizzle Kit
push: https://orm.drizzle.team/docs/drizzle-kit-push - Drizzle Kit
pull: https://orm.drizzle.team/docs/drizzle-kit-pull - Drizzle Custom Migrations: https://orm.drizzle.team/docs/kit-custom-migrations
- Drizzle Migrate from Prisma: https://orm.drizzle.team/docs/migrate/migrate-from-prisma
- Drizzle Migrate from TypeORM: https://orm.drizzle.team/docs/migrate/migrate-from-typeorm
- Drizzle ORM GitHub Releases: https://github.com/drizzle-team/drizzle-orm/releases
- Prisma Migrate Overview: https://docs.prisma.io/docs/orm/prisma-migrate
- Prisma Relations: https://www.prisma.io/docs/orm/prisma-schema/data-model/relations
- Prisma Client Type Safety: https://www.prisma.io/docs/orm/prisma-client/type-safety
- TypeORM Getting Started: https://typeorm.io/docs/getting-started/
- TypeORM Decorator Reference: https://typeorm.io/docs/help/decorator-reference/
- TypeORM Relations: https://typeorm.io/docs/relations/relations/
- TypeORM Find Options: https://typeorm.io/docs/working-with-entity-manager/find-options/
- TypeORM Select Query Builder: https://typeorm.io/docs/query-builder/select-query-builder/