选错 ORM 的代价,后期修复起来非常昂贵。在将生产级 Node.js SaaS 产品全部用这三种 ORM 交付后,这里是一份诚实的对比。
参赛选手
| Prisma | Drizzle | TypeORM | |
|---|---|---|---|
| 星标数 (2025) | 40k+ | 25k+ | 34k+ |
| 首次发布 | 2019 | 2022 | 2016 |
| 查询风格 | Schema DSL | 类 SQL API | Active Record / Data Mapper |
| 运行时 | Node.js + 边缘* | Node.js + 边缘 | Node.js |
| 迁移 | Prisma Migrate | Drizzle Kit | 内置 / TypeORM CLI |
Prisma
Prisma 使用自己的 schema 定义语言(schema.prisma)生成完全类型化的客户端。你只需定义一次模型,就能在每次查询中获得自动补全。
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id Int @id @default(autoincrement())
title String
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}
// 每次查询都是完全类型化的
const user = await prisma.user.findUnique({
where: { email: "[email protected]" },
include: { posts: { where: { published: true } } }
});
// user: { id: number; email: string; posts: Post[]; ... } | null
迁移在 Prisma 中是头等公民。你编辑 schema,运行 prisma migrate dev,就会得到一个版本化的 SQL 迁移文件并提交到源码管理。prisma migrate deploy 在 CI/CD 中应用待处理的迁移。
优势:
- 最佳的开发者体验和自动补全
- 优秀的文档和庞大的社区
- Prisma Studio 用于可视化数据浏览
- 通过中间件内置软删除/审计日志
劣势:
- Prisma 引擎作为独立的 sidecar 二进制文件运行——在无服务器环境中增加冷启动延迟和内存开销
- 无法完全退回到原始 SQL 而不丢失类型安全(使用
$queryRaw配合Prisma.sql模板标签) - Schema DSL 是另一种需要学习的语言
何时使用 Prisma: 当你希望为整天编写 TypeScript 的团队提供最流畅的 DX,你不在边缘环境中,并且你重视迁移工作流。
Drizzle
Drizzle 在 TypeScript 中定义 schema,而不是使用单独的 DSL。查询使用一个紧密模仿 SQL 的构建器 API 编写。
import { pgTable, serial, text, boolean, integer, timestamp } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
email: text("email").notNull().unique(),
name: text("name"),
createdAt: timestamp("created_at").defaultNow().notNull()
});
export const posts = pgTable("posts", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
published: boolean("published").default(false).notNull(),
authorId: integer("author_id").references(() => users.id).notNull()
});
// 类 SQL,完全类型化
const usersWithPosts = await db
.select()
.from(users)
.leftJoin(posts, eq(posts.authorId, users.id))
.where(eq(users.email, "[email protected]"));
Drizzle 生成纯 SQL,没有中间引擎,使其非常适合 Cloudflare Workers、Vercel Edge Functions 和低延迟的无服务器环境。
迁移使用 drizzle-kit:
pnpm drizzle-kit generate # 比较 schema 与数据库 → 输出 SQL 迁移
pnpm drizzle-kit migrate # 应用待处理的迁移
优势:
- 无 sidecar 二进制文件——可在任何 JavaScript 运行的地方运行(边缘、无服务器、Bun、Deno)
- 最薄的抽象层:如果你懂 SQL,你就懂 Drizzle
- 原始查询基准测试中最快
- 包含 Drizzle Studio
劣势:
- 生态较年轻——第三方插件较少
- 迁移工具不如 Prisma 完善(但改进迅速)
- 复杂连接的学习曲线高于 Prisma 的 includes
何时使用 Drizzle: 当你需要边缘运行时支持,想要对 SQL 的最小抽象,或者你的团队习惯编写原始 SQL 并希望获得类型化结果。
TypeORM
TypeORM 早于 TypeScript 严格模式的改进,显得有些过时。它使用装饰器定义实体:
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
email: string;
@Column({ nullable: true })
name: string;
}
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column({ default: false })
published: boolean;
@ManyToOne(() => User, user => user.posts)
author: User;
}
TypeORM 支持两种模式:Active Record(实体拥有 .save()、.find() 方法)和 Data Mapper(由仓库执行工作)。Data Mapper 模式与 NestJS 等依赖注入框架集成得更好。
优势:
- 最成熟——庞大的生态系统,大量教程
- 原生 NestJS 集成
- 支持最广泛的数据库(包括 MongoDB)
劣势:
- 装饰器元数据需要
reflect-metadatashim 和特定的tsconfig设置 - 严格的 TypeScript 支持不完整——某些查询返回
any - 在急加载中存在 N+1 查询问题的活跃 issue
- 开发速度慢于 Prisma / Drizzle
何时使用 TypeORM: 当你使用 NestJS 构建并希望获得框架原生集成,或者你在扩展现有的 TypeORM 代码库。
性能对比
对于典型的 SaaS 读取端点(获取用户 + 5 个关联):
| ORM | 冷启动 (Lambda) | P99 查询时间 |
|---|---|---|
| Drizzle | ~5ms | ~2ms |
| Prisma (引擎已缓存) | ~15ms | ~4ms |
| Prisma (冷启动) | ~120ms | ~4ms |
| TypeORM | ~20ms | ~5ms |
Prisma 的冷启动惩罚在无服务器环境中最为明显。在长时间运行的 Node.js 进程(EC2、Railway、Fly.io)中,Prisma 的引擎已经预热,性能差距会缩小。
决策矩阵
| 场景 | 推荐 |
|---|---|
| 基于 VPS/容器的 NestJS 单体应用 | Prisma 或 TypeORM |
| 使用 Vercel 或 Cloudflare 的 Next.js / Remix 应用 | Drizzle |
| 基于 Lambda 的微服务 | Drizzle |
| 团队偏好 SQL,讨厌 DSL | Drizzle |
| 追求极致 DX,团队级自动补全 | Prisma |
| 现有 NestJS + TypeORM 代码库 | 保留 TypeORM |
迁移路径
如果你正在使用 TypeORM 并考虑切换,Drizzle 更容易迁移,因为两者都使用显式 SQL。Prisma 需要先重写为其 schema DSL。
推荐的迁移方法:
- 在非关键服务上并行运行两个 ORM
- 在预发布环境中比较查询输出
- 一次迁移一个服务,从读取密集型服务开始,Drizzle 的速度优势在此显现
- 在写入密集型服务上保留 TypeORM,直到你建立信心