去年春天我在一个 SaaS 副业项目中三周后接入了 Supabase,面临一个岔路口:Drizzle 还是 Prisma。我在 Seahawk 大约四十多个客户项目上用过 Prisma。熟悉的地盘。但项目里一个初级开发者一直推崇 Drizzle,说实话我最初有点不以为然。"它更新。证实不足。我们就用 Prisma 吧。"
我当时就这样打发掉它是错的。
我们最后在同一代码库的不同部分都试了一遍(乱是乱,但很有启发)。我学到的东西改变了我对 ORM 选择的整个思维。如果你在 Supabase 上构建,真的不确定选哪个,这就是我希望当初有过的文章。
---
你实际上在选择什么
这两个工具解决同一个表面问题:你不想每次操作数据库都写原始 SQL。但它们来自完全不同的哲学。
Prisma 把你的 schema 当作唯一的真实来源。你定义一个 schema.prisma 文件,运行 prisma generate,得到一个完全类型化的客户端。它几乎完全抽象了 SQL。你很少用表和联接的思路思考;你用 models 和 relations 来思考。
Drizzle 离 SQL 更近。你的 schema 用 TypeScript 定义,查询看起来像 SQL,中间没有独立的 CLI 生成的客户端。它被描述为"感觉像 SQL 的 TypeScript ORM",这个说法是真的准确。
都不是客观上更好。就这么说。选择取决于你的团队、查询模式,以及你对 Supabase 自身工具做繁重工作的信任程度。
---
Supabase 的背景改变了一切
大多数对比文章都忽略了一点:Supabase 不是一个空白的 Postgres 数据库。它附带行级安全、实时订阅、自动生成的 REST 和 GraphQL API,以及自己的 JavaScript 客户端。在碰到 ORM 之前,你通常已经在通过 supabase-js 做很多事了。
所以真正的问题不仅仅是"哪个 ORM 更好?"而是"我应该有多少查询逻辑通过 ORM 运行,又有多少通过 Supabase 客户端直接运行?"
我见过团队选择 Prisma,几乎完全忽视 supabase-js,然后想知道为什么他们的 RLS 策略没有触发。原因是 Prisma 通过连接字符串直接连接到 Postgres。它绕过了 Supabase PostgREST 层。你的 RLS 规则?只有在你告诉 Prisma 在会话级别 SET LOCAL role = authenticated 时才会被强制执行。这不难设置,但你得知道这个东西的存在。
Drizzle 有同样的问题。直接 Postgres 连接,相同的绕过行为。但因为 Drizzle 感觉更接近原生 SQL,开发者往往更清楚他们在直接与 Postgres 对话,而不是通过 Supabase 抽象层。
---
打包体积和冷启动:无服务器的论证
如果你部署到 Vercel Edge Functions、Cloudflare Workers 或甚至标准的 Vercel Serverless Functions,打包体积是一个真实的问题。
Prisma 生成的客户端…很庞大。查询引擎本身就是一个二进制文件,会和你的部署一起打包。有一段时间,Prisma 在 Vercel 上的部署产生的包超过 40MB。他们通过 Prisma Accelerate 和新的引擎选项大幅改进了这一点,但你仍然在与重量劣势搏斗。我们在 2023 年末发布的一个 Seahawk 金融科技项目中,无服务器函数的冷启动显著更慢。我们测量过:用 Prisma 大约 800ms 的冷启动,而迁移到 Drizzle 后同一服务低于 200ms。
Drizzle 很小。小到令人尴尬的程度。没有二进制引擎。没有运行时代码生成。它编译成最小化的 JavaScript,你的部署保持精简。对于边缘运行时,这是当前的显而易见的选择。
话说回来,如果你运行传统的 Node.js 服务器(Express、Fastify、在普通 VPS 上的标准 Next.js 应用),这个差距就不那么重要了。持久连接池不在乎冷启动。
---
开发者体验:Prisma 仍然胜出的地方
我会坦白说。Prisma 的 DX 很难被击败。
schema 文件确实令人愉快。迁移通过 prisma migrate dev 处理,就这样工作了。Prisma Studio(GUI)在我需要快速检查数据时,为我省了无数小时,省去了在 Supabase 仪表板中挖掘的时间。从生成的客户端而来的 TypeScript 类型很彻底。你可以在嵌套关系、where 子句、select 形状上获得自动完成。
Drizzle 的 TypeScript 支持也非常出色,但需要更多的前期思考。你在 TypeScript 文件中编写 schema,这实际上是我在哲学上更喜欢的。没有单独的 .prisma 语法要学。但查询生成器需要适应。像带聚合的复杂联接这样的事情不如 Prisma 的 include 语法那么直观。
Schema 迁移:一个真正的区别
Prisma 自动生成迁移 SQL 文件并跟踪它们。Drizzle 也做这个,用 drizzle-kit,但工作流感觉有点更手动。你运行 drizzle-kit generate:pg,得到一个 SQL 文件,然后自己应用它(或使用 drizzle-kit push 快速原型化)。更少的魔法,更多的控制。
对于初级开发者,Prisma 每次都赢这里。对于想要准确理解正在运行哪些 SQL 的独立开发者,Drizzle 的方式令人满意,Prisma 不是。
---
原生查询能力和复杂场景
回到 2019 年,一个客户给了我一个需要非常复杂聚合的简报:运行总计、窗口函数、条件分组。我那时在用 Prisma,我几乎立即触到了天花板。Prisma 的 queryRaw 存在,但在一个原本抽象化的代码库中掉入原生 SQL 感觉像是作弊,而且你失去了所有类型安全。
Drizzle 在这方面处理得好得多。窗口函数、CTE、侧向联接:它要么有一个一等公民的构建器,要么你可以掉入 SQL 片段而不失去你的 TypeScript 上下文。对于有真正复杂报告或分析查询的 Supabase 项目,Drizzle 给你更多增长空间。
话虽如此,80% 的 CRUD 应用不需要任何这个。如果你的项目是"用户创建帖子,帖子有评论",Prisma 的表现力关系查询更快写出来,其他开发者一眼更容易读懂。
---
何时选择各个平台
让我直接说这个,因为我见过太多人为了找到"客观正确"的答案把自己搞得一团糟。
选择 Drizzle 如果:
- 你部署到边缘或无服务器运行时,打包体积和冷启动很重要
- 你的团队熟悉 SQL,你想要你实际运行的查询的透明度
- 该项目具有复杂的查询需求(报告、分析、非标准聚合)
- 你是独立开发者或小型团队,想要最小化抽象开销
在以下情况下选择 Prisma:
- 你运行传统的服务端 Node.js 设置,带有连接池
- 你的团队有初级开发者,他们受益于引导式的 schema-first 工作流
- 项目是 CRUD 密集型的,关系复杂度中等
- 你想要一个成熟的生态系统,拥有更多第三方工具、示例和 Stack Overflow 答案
还有一点值得一提:Prisma 的文档更好。好得多。Drizzle 的文档在过去一年改进了很多,但 Prisma 有更长的时间来积累教程、指南和社区资源。如果你边学边做,这个差距是真实存在的。
---
Supabase 的实用设置说明
无论你选择哪个,连接到 Supabase 时都有几点普遍适用。
- 使用连接池化 URI,而不是直接连接。Supabase 通过 PgBouncer 提供连接池化。对于无服务器,始终使用这个。Prisma 推荐的无服务器
DATABASE_URL应该指向 6543 端口上的池化端点。 - 使用 PgBouncer 时禁用预处理语句。Prisma 需要在 URL 后附加 ?pgbouncer=true。Drizzle 需要在 Postgres.js 或 node-postgres 配置中设置
prepare: false。跳过这一步,你会在生产环境中看到神秘的错误。 - RLS 是你的朋友,但你必须配置会话。如果你想让 RLS 策略应用于 ORM 查询,你需要在会话级别设置 Postgres 角色和 JWT 声明。这不是你免费获得的样板代码。
- 不要与 Supabase 的优势对抗。使用
supabase-js进行身份验证、实时和存储。在 Supabase 客户端的过滤不足的复杂数据查询中使用你的 ORM。它们可以在同一项目中共存。
---
FAQ
Drizzle 在 2024 年生产就绪吗?
是的。它正在生产环境中被不仅仅是周末副项目的公司的团队使用。自 2023 年末以来,API 已经足够稳定以支持严肃的工作。我仍然会说 Prisma 更"经过实战考验",只是因为它存在的时间更长,但 Drizzle 已经不是一个风险了。
我可以在同一个项目中同时使用两者吗?
从技术上讲可以。我们曾短暂地这样做过(意外地,不是作为一种策略)。不要这样做。认知开销不值得,让两个不同的迁移系统接触同一个数据库是在自找麻烦。选择一个。
Prisma 可以与 Supabase Edge Functions 配合使用吗?
Prisma 和 Supabase Edge Functions(运行在 Deno 上)的关系一直很复杂。Prisma 的引擎在 Deno 中无法原生运行。使用 Prisma Accelerate 或外部池化设置可以解决这个问题,但它增加了可变部分。Drizzle 在 Deno 环境中没有这样的问题。
类型安全呢?它们可比吗?
两者都生成 TypeScript 类型,都能很好地与 TypeScript 项目集成。Drizzle 的类型来自你的 TypeScript schema 定义。Prisma 的类型来自生成的客户端。根据我的经验,Prisma 的嵌套关系类型开箱即用时稍微更符合人体工学,但 Drizzle 的推断已经大幅追上来了。
运行时速度如何?
Drizzle 有真正的性能优势,因为你的查询和 Postgres 线协议之间的运行时开销更少。在基准测试中,差异是可测量的。在大多数真实应用中,它被到数据库的网络延迟所压倒。不要主要因为原始查询速度来选择 ORM。
---
真正的答案
如果你在边缘计算/无服务器 Supabase 项目中工作,或者想要更接近底层,就用 Drizzle。如果是团队环境、CRUD 密集的应用,或者开发者入职速度很重要,就用 Prisma。
我现在用 Drizzle 的频率比一年前高。但我不后悔在 Prisma 上花的那些时间。它让我成为了更好的开发者,部分原因是它足够有主见,迫使我理解为什么会触及它的限制。
哪一个都不会决定你项目的成败。决定因素是你的 Schema 设计和索引决策。选择最适合你团队的那个,然后开始构建。
