Prisma Patterns 是一份面向 TypeScript 后端的 Prisma ORM 生产模式与非显而易见陷阱的参考。它并非一个可安装的库,而是一组可对照使用的规则、代码示例和反模式说明。
用途 它用于在以下场景中提供指导:设计或修改 Prisma schema 模型与关系;编写查询、事务或分页逻辑;使用 updateMany、deleteMany 等批量操作;运行或规划数据库迁移;部署到无服务器环境(Vercel、Lambda、Cloudflare Workers);实现软删除或多租户行过滤。
版本前提 资料强调在套用模式前先检查版本,命令为 npx prisma --version。不同大版本的 API 存在差异:relationJoins 可通过 JOIN 而非独立查询加载关系,但在大型 1:N 关系或深层 include 时可能造成行数膨胀,建议对两种方式做基准测试;omit 字段修饰符和 prisma.$extends Client Extensions API 是后续加入的;较新的安装中包名可能是 prisma 而非 @prisma/client,PrismaClient 可能需要驱动适配器(如 @prisma/adapter-pg),datasource.url 可能位于 prisma.config.ts 而非 schema.prisma。CLI 命令(migrate dev、migrate deploy、generate)在各版本间保持不变。
主要能力与核心概念 ID 策略:@default(cuid()) 为默认选择,URL 安全、可排序、无碰撞;@default(uuid()) 用于需要与非 Prisma 系统互操作的场景,但高写入表会因随机 UUID 导致 B-tree 索引碎片化;@default(autoincrement()) 用于内部连接表、审计日志,避免用于公开 ID。 Schema 默认值:@unique 已创建索引,无需再加 @@index;应为每个外键以及 WHERE 或 ORDER BY 中使用的列添加 @@index;若软删除是可预见需求,应提前声明 deletedAt DateTime?,否则后续在线上表添加需要迁移;updatedAt @updatedAt 仅在 update 和 upsert 时由 Prisma 自动设置。 include 与 select:include 返回所有标量字段加指定关系,适合需要大部分字段加关系的场景,但在宽表上可能过度获取;select 只返回指定字段,适合热路径、大表、避免过度获取。资料建议不要从 API 响应直接返回原始 Prisma 实体,而应映射到响应 DTO 以控制暴露字段。 事务形式选择:独立、无相互依赖的操作使用数组形式;后续步骤依赖前一步结果时使用交互形式;涉及外部调用(邮件、HTTP)时应放在事务之外。交互形式应只使用 tx 客户端,不要使用外层 prisma 客户端。 PrismaClient 单例:每个 PrismaClient 实例会打开自己的连接池,应只实例化一次。资料给出基于适配器的初始化(较新安装可能需要)和直接初始化(较旧安装)两种方式,并使用 globalThis 模式防止热重载时产生重复实例。 N+1 问题:在循环中加载关系会为每行发出一次查询,应改为单次查询的 include 形式。在 Prisma 5+ relationJoins 下,include 形式使用单个 JOIN,但在大型 1:N 集合上可能增加结果集大小,建议对两种方式做基准测试。
使用方式与代码示例 游标分页:资料给出 getPosts 示例,使用 take: limit + 1 后 pop 来判断 hasNextPage,避免额外 count 查询;orderBy 中除 createdAt 外还应加入唯一字段(如 id)作为次级排序,防止时间戳重复时分页不稳定;仅在需要跳转到任意页(如管理表格)时使用偏移分页。 软删除:应始终显式过滤,不要依赖中间件;示例使用 where: { deletedAt: null } 查询,并通过 update 设置 deletedAt 为当前时间或 null 来删除与恢复。 错误处理:捕获 Prisma.PrismaClientKnownRequestError,常见代码为 P2002 唯一约束冲突、P2025 未找到、P2003 外键约束冲突;应在服务边界捕获并转换为领域错误,不要向 API 消费者暴露原始 Prisma 消息。 无服务器连接池:应将连接参数直接嵌入 DATABASE_URL,因为字符串拼接在 URL 已有查询参数(如 ?schema=public)时会出错;示例包括 connection_limit=1&pool_timeout=20 以及配合外部连接池(PgBouncer、Supabase pooler)的 pgbouncer=true&connection_limit=1。
反模式与限制 updateMany 返回的是计数而非记录,deleteMany 同理返回 { count: n },不会返回被删除的行;需要受影响记录时应先查 ID,再更新,再按 ID 查询。 $transaction 交互形式默认 5 秒超时,事务内进行外部调用会超时并报“Transaction already closed”;应将外部调用移到事务外,仅在批量处理确实需要时提高 timeout。 migrate dev 可能重置数据库:它检测到 schema 漂移时可能提示重置并删除所有数据,绝不能在共享开发、预发或生产环境使用;migrate deploy 在除本地单人开发外的环境都安全;可用 migrate diff 检查漂移而不应用。 手动编辑迁移文件会破坏后续部署:Prisma 对每个迁移文件做校验和,应用后编辑会导致已运行过原文件的环境出现 P3006 校验和不匹配,应改为创建新迁移。 破坏性 schema 变更需要多步迁移:为现有列添加 NOT NULL 或重命名列若在单个迁移中完成会锁表或丢数据,应使用 expand-and-contract 三步流程。 @updatedAt 不会在 updateMany 时触发,批量写入会使其保持旧值,需手动设置 updatedAt: new Date()。 软删除与 findUniqueOrThrow:findUniqueOrThrow 仅在行不存在于数据库时抛出 P2025,软删除的行仍存在且会被返回;且 where 中同时写 id 和 deletedAt: null 会因不是复合唯一约束而类型报错,应改用 findFirstOrThrow。 deleteMany 不带 where 会删除所有行,应始终提供 where。
最佳实践摘要 CI/CD 使用 migrate deploy,migrate dev 仅本地使用;将实体映射为响应 DTO;在服务边界捕获 PrismaClientKnownRequestError;优先使用 *OrThrow 方法,过滤非唯一字段时用 findFirstOrThrow;无服务器环境使用 connection_limit=1 加外部连接池;deleteMany 始终提供 where;updateMany 中手动设置 updatedAt。
相关资料还列出相关技能:nestjs-patterns、postgres-patterns、database-migrations、backend-patterns。
适用场景 适合使用 Prisma ORM 的 TypeScript 后端开发者,在 schema 设计、查询与事务编写、迁移规划、无服务器部署、软删除与多租户过滤等环节参考。
限制 资料未提供该技能的安装方式、收费或价格信息、平台支持范围、效果数据或具体使用步骤,这些内容暂未提供。