关于 api-database-prisma
面向使用 TypeScript 和 Prisma ORM 的开发者,提供类型安全查询、Schema 优先建模与迁移的实践指南。内容涵盖关联查询、事务和连接管理,并强调避免 N+1 查询及开发环境连接耗尽。
主要功能
- 01PrismaClient 单例与连接管理
- 02类型安全的 CRUD 与分页
- 03关联查询及 N+1 防范
- 04Schema 建模与声明式迁移
- 05嵌套写入与事务处理
适用场景
- 01为 TypeScript 应用建立 Prisma 数据访问层
- 02设计模型、关系、索引和枚举
- 03查询关联数据并避免 N+1
- 04以事务执行多项原子写入
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents-inc/ skills --skill api-database-prisma安装后在 Claude Code 中输入/api-database-prisma确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.yamlreference.mdexamples/core.mdrelations.mdtransactions.md详细介绍
原文在翻译后有更新,译文可能不是最新。
使用 Prisma ORM 的数据库
快速指南: 使用 Prisma ORM 执行类型安全的数据库查询,并自动生成 TypeScript 类型。采用以 Schema 为先的设计和声明式迁移。使用
include查询关联数据,使用$transaction执行原子操作。开发环境中必须使用单例模式,以避免连接耗尽。在交互式事务回调中,始终使用tx(而非prisma)。
<critical_requirements>
重要:使用此技能前
所有代码都必须遵循项目在 CLAUDE.md 中的约定(kebab-case、命名导出、导入顺序、
import type、命名常量)
(开发环境中必须对 PrismaClient 使用单例模式,以防止热重载导致连接耗尽)
(交互式事务回调中必须使用 tx 参数(而非 prisma),以确保原子性)
(关系查询必须使用 include 或嵌套 select——在同一个查询中获取关联数据,以避免 N+1 问题)
(所有外键关系都必须使用显式的 fields 和 references 定义 @relation)
</critical_requirements>
自动检测: prisma、@prisma/client、PrismaClient、prisma.schema、prisma migrate、findUnique、findMany、include、$transaction
适用场景:
- 执行类型安全的数据库查询,并自动生成 TypeScript 类型
- 采用以 Schema 为先的开发方式和声明式迁移
- 需要强关系数据建模的应用
- 使用 Prisma Studio GUI 快速构建原型
不适用场景:
- 复杂查询需要原生 SQL 性能(Prisma 会带来额外开销)
- Edge/无服务器环境要求冷启动开销极低(可考虑更轻量的 ORM)
- 非 TypeScript 项目(无法发挥主要优势)
- 需要对生成的 SQL 进行细粒度控制
涵盖的关键模式:
- PrismaClient 单例(确保开发环境热重载安全)
- 使用类型安全筛选器和分页执行 CRUD 操作
- 使用
include和嵌套select查询关联数据 - 事务(嵌套写入、批处理、交互式事务)
- Schema 设计(模型、关系、枚举、索引)
详细资源:
- examples/core.md - 单例设置、CRUD、筛选、分页
- examples/relations.md - 关系查询、include、避免 N+1 问题
- examples/transactions.md - 原子操作、交互式事务、错误处理
- reference.md - 决策框架、反模式、性能
理念
Prisma ORM 提供声明式 Schema 语言,可生成类型安全的数据库客户端。Schema 是数据模型、TypeScript 类型和迁移的唯一事实来源。
核心原则:
- 以 Schema 为先的设计 - 在
schema.prisma中定义模型,再生成其他所有内容 - 全面保障类型安全 - 所有查询都根据 Schema 完整提供类型
- 声明式迁移 - Schema 变更会自动生成迁移 SQL
- 直观的 API - 查询读起来就像英文(
prisma.user.findMany())
核心模式
模式 1:PrismaClient 单例
使用单例模式,防止开发环境热重载期间连接池耗尽。否则,每次热重载都会创建一个新的 PrismaClient,并拥有自己的连接池,很快就会耗尽数据库连接。
// lib/db/client.ts
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
const createPrismaClient = () => {
return new PrismaClient({
log:
process.env.NODE_ENV === "development"
? ["query", "error", "warn"]
: ["error"],
});
};
export const prisma = globalForPrisma.prisma ?? createPrismaClient();
if (process.env.NODE_ENV !== "production") {
globalForPrisma.prisma = prisma;
}
优点: globalThis 可在热重载期间保持状态,条件式日志记录可避免在生产环境产生多余日志
有关无服务器连接模式,请参阅 examples/core.md。
模式 2:Schema 设计
定义包含关系、约束和默认值的模型。Schema 是唯一事实来源。
model User {
id String @id @default(cuid())
email String @unique
name String?
role Role @default(USER)
posts Post[]
profile Profile?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("users")
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([authorId])
@@map("posts")
}
优点: 使用 cuid() 生成抗冲突 ID,@updatedAt 自动跟踪变更,@relation 配合 onDelete: Cascade 可避免孤儿记录,在外键上使用 @@index,使用 @@map 将代码中的 PascalCase 映射为数据库表名中的 snake_case
模式 3:带类型安全筛选条件的 CRUD
所有查询都会根据你的架构进行完整类型检查。关键操作:
const DEFAULT_PAGE_SIZE = 20;
const MAX_PAGE_SIZE = 100;
// Find by unique field - returns T | null
const user = await prisma.user.findUnique({
where: { email: "[email protected]" },
});
// Find many with filters + pagination
const users = await prisma.user.findMany({
where: {
role: { in: ["USER", "MODERATOR"] },
createdAt: { gte: new Date("2024-01-01") },
},
orderBy: { name: "asc" },
take: DEFAULT_PAGE_SIZE,
});
// Upsert - atomic create-or-update
const upserted = await prisma.user.upsert({
where: { email: "[email protected]" },
create: { email: "[email protected]", name: "Alice" },
update: { name: "Alice Updated" },
});
优点: 类型安全的操作可在编译时捕获错误;findUnique 返回 T | null,强制处理空值;upsert 是原子操作
请参阅 examples/core.md,了解完整的 CRUD 操作、筛选和分页模式。
模式 4:关系查询
使用 include 或嵌套 select 高效获取相关数据,避免 N+1 查询。
// Include related records - single query
const userWithPosts = await prisma.user.findUnique({
where: { id: userId },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: "desc" },
take: 10,
},
profile: true,
},
});
// Select specific fields only - smaller payload
const userSummary = await prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
name: true,
posts: { select: { id: true, title: true } },
},
});
优点: 单次查询可避免 N+1;include 会获取所有字段;select 可减少数据量。切勿针对每条记录循环查询——应改用 include 或 select。
请参阅 examples/relations.md,了解关系筛选、多对多关系、自关联、include 与 select 的比较,以及 N+1 反模式。
模式 5:事务
确保多个写入操作具有原子性。可用类型有三种:
// Nested writes - implicit transaction (cleanest for related records)
const user = await prisma.user.create({
data: {
email: "[email protected]",
name: "Alice",
profile: { create: { bio: "Developer" } },
posts: { create: [{ title: "First Post", published: true }] },
},
include: { profile: true, posts: true },
});
// Interactive transaction - ALWAYS use tx, never prisma
return await prisma.$transaction(async (tx) => {
const sender = await tx.account.update({
where: { id: fromId },
data: { balance: { decrement: amount } },
});
if (sender.balance < MINIMUM_BALANCE) throw new Error("Insufficient funds");
return await tx.account.update({
where: { id: toId },
data: { balance: { increment: amount } },
});
});
优点: 嵌套写入可用于相关记录;交互式事务支持业务逻辑,并能自动回滚。在回调中使用 prisma 而非 tx 会绕过事务上下文。
请参阅 examples/transactions.md,了解批量事务、错误处理、乐观并发和事务选项。
模式 6:连接管理
在 beforeExit 时处理 $disconnect(),以防止连接泄漏。对于无服务器环境,请通过单独的 DATABASE_URL_WITH_POOLER 环境变量使用连接池(PgBouncer、Prisma Accelerate)。
请参阅 examples/core.md,了解优雅关闭和无服务器连接模式。
<red_flags>
危险信号
高优先级问题:
- 每次导入时都创建 PrismaClient——热重载期间会耗尽数据库连接
- 在交互式事务中使用
prisma而非tx——会绕过事务上下文 - 在循环中执行 N+1 查询——应改用
include或select - 缺少
@relation属性——外键不明确会导致迁移错误
中优先级问题:
- 经常用于筛选的列没有索引——随着数据增长,查询会变慢
- 在大型表上使用偏移分页——性能会线性下降
- 缺少
onDelete级联操作——删除父记录时会产生孤立记录 - 使用
include获取所有字段,但实际只需要部分字段——应改用select
注意事项和边缘情况:
createMany不会返回创建的记录(在 PostgreSQL/CockroachDB/SQLite 上使用createManyAndReturn)updateMany和deleteMany不会自动更新@updatedAt字段- 隐式多对多关系表不能包含额外字段——请使用显式联接模型
Json字段的类型为JsonValue——需要在解析边界进行运行时验证Decimal字段返回Prisma.Decimal类型——使用.toNumber()进行转换- 交互式事务的默认超时时间为 5 秒——使用
timeout选项延长 - 交互式事务会占用数据库连接——请尽量缩短事务时间
- 未指定
orderBy的findFirst会返回不确定的结果 - 枚举变更需要通过迁移添加或删除值
</red_flags>
<critical_reminders>
重要提醒
所有代码都必须遵循 CLAUDE.md 中的项目约定
(在开发环境中,必须对 PrismaClient 使用单例模式,以防止热重载导致连接耗尽)
(在交互式事务回调中,必须使用 tx 参数(而非 prisma),以确保原子性)
(关系查询必须使用 include 或嵌套 select——在同一查询中获取关联数据,避免 N+1 问题)
(必须为所有外键关系使用显式的 fields 和 references 定义 @relation)
如果不遵循这些规则,将会耗尽数据库连接、破坏事务原子性、导致 N+1 性能问题,并造成关系定义不明确。
</critical_reminders>