prisma-orm by bobmatnyc/claude-mpm-skills
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill prisma-orm适用于 TypeScript 的现代数据库工具包,采用模式优先开发、自动生成的类型安全客户端和强大的迁移系统。
npm install prisma @prisma/client
npx prisma init
# 1. 定义模式
# 编辑 prisma/schema.prisma
# 2. 创建迁移
npx prisma migrate dev --name init
# 3. 生成客户端
npx prisma generate
# 4. 打开 Studio
npx prisma studio
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([authorId])
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// 创建
const user = await prisma.user.create({
data: {
email: 'alice@example.com',
name: 'Alice',
posts: {
create: { title: 'First Post', content: 'Hello World' }
}
},
include: { posts: true }
});
// 带过滤器的读取
const users = await prisma.user.findMany({
where: { email: { contains: '@example.com' } },
include: { posts: { where: { published: true } } },
orderBy: { createdAt: 'desc' },
take: 10
});
// 更新
await prisma.user.update({
where: { id: userId },
data: { name: 'Bob' }
});
// 删除
await prisma.user.delete({ where: { id: userId } });
model Product {
id Int @id @default(autoincrement())
sku String @unique
name String
description String? // 可选字段
price Decimal @db.Decimal(10, 2)
inStock Boolean @default(true)
quantity Int @default(0)
tags String[] // 数组字段 (PostgreSQL)
metadata Json? // JSON 字段
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([sku])
@@index([name, inStock])
}
一对多:
model User {
id String @id @default(cuid())
posts Post[]
}
model Post {
id String @id @default(cuid())
author User @relation(fields: [authorId], references: [id])
authorId String
@@index([authorId])
}
多对多:
model Post {
id String @id @default(cuid())
categories Category[] @relation("PostCategories")
}
model Category {
id String @id @default(cuid())
name String @unique
posts Post[] @relation("PostCategories")
}
一对一:
model User {
id String @id @default(cuid())
profile Profile?
}
model Profile {
id String @id @default(cuid())
bio String
user User @relation(fields: [userId], references: [id])
userId String @unique
}
自关联:
model User {
id String @id @default(cuid())
following User[] @relation("UserFollows")
followers User[] @relation("UserFollows")
}
// 创建嵌套关系
const user = await prisma.user.create({
data: {
email: 'bob@example.com',
profile: {
create: { bio: '软件工程师' }
},
posts: {
create: [
{ title: '文章 1', content: '内容 1' },
{ title: '文章 2', content: '内容 2' }
]
}
}
});
// 使用嵌套操作更新
await prisma.user.update({
where: { id: userId },
data: {
posts: {
create: { title: '新文章' },
update: {
where: { id: postId },
data: { published: true }
},
delete: { id: oldPostId }
}
}
});
顺序(交互式):
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: { email: 'alice@example.com' }
});
await tx.post.create({
data: { title: '文章', authorId: user.id }
});
// 如果抛出错误则回滚
if (someCondition) {
throw new Error('回滚事务');
}
});
批量(并行):
const [deletedPosts, updatedUser] = await prisma.$transaction([
prisma.post.deleteMany({ where: { published: false } }),
prisma.user.update({
where: { id: userId },
data: { name: '已更新' }
})
]);
聚合:
const result = await prisma.post.aggregate({
_count: { id: true },
_avg: { views: true },
_sum: { likes: true },
_max: { createdAt: true },
where: { published: true }
});
const grouped = await prisma.post.groupBy({
by: ['authorId'],
_count: { id: true },
_avg: { views: true },
having: { views: { _avg: { gt: 100 } } }
});
原始 SQL:
// 原始查询
const users = await prisma.$queryRaw<User[]>`
SELECT * FROM "User" WHERE email LIKE ${`%${search}%`}
`;
// 执行
await prisma.$executeRaw`
UPDATE "Post" SET views = views + 1 WHERE id = ${postId}
`;
# 创建并应用迁移
npx prisma migrate dev --name add_user_role
# 重置数据库(警告:删除所有数据)
npx prisma migrate reset
# 查看迁移状态
npx prisma migrate status
# 应用待处理的迁移
npx prisma migrate deploy
# 生成客户端(在 CI/CD 中)
npx prisma generate
# 推送模式而不进行迁移(仅限开发)
npx prisma db push
# 从现有数据库拉取模式
npx prisma db pull
// lib/prisma.ts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma;
}
服务器组件:
// app/users/page.tsx
import { prisma } from '@/lib/prisma';
export default async function UsersPage() {
const users = await prisma.user.findMany({
include: { posts: { take: 5 } }
});
return (
<ul>
{users.map(u => (
<li key={u.id}>{u.name} - {u.posts.length} 篇文章</li>
))}
</ul>
);
}
服务器操作:
// app/actions.ts
'use server';
import { prisma } from '@/lib/prisma';
import { revalidatePath } from 'next/cache';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const authorId = formData.get('authorId') as string;
await prisma.post.create({
data: { title, authorId }
});
revalidatePath('/posts');
}
import { PrismaClient } from '@prisma/client';
import express from 'express';
const app = express();
const prisma = new PrismaClient();
app.get('/users/:id', async (req, res) => {
const user = await prisma.user.findUnique({
where: { id: req.params.id },
include: { posts: true }
});
if (!user) return res.status(404).json({ error: '未找到' });
res.json(user);
});
app.listen(3000);
// ❌ N+1 查询
const users = await prisma.user.findMany();
for (const user of users) {
const posts = await prisma.post.findMany({
where: { authorId: user.id }
});
}
// ✅ 使用 include 的单次查询
const users = await prisma.user.findMany({
include: { posts: true }
});
// ✅ 选择特定字段
const users = await prisma.user.findMany({
select: { id: true, email: true, posts: { select: { title: true } } }
});
// 基于游标(推荐用于大型数据集)
const posts = await prisma.post.findMany({
take: 10,
cursor: lastPostId ? { id: lastPostId } : undefined,
skip: lastPostId ? 1 : 0,
orderBy: { createdAt: 'desc' }
});
// 基于偏移量(简单但较慢)
const posts = await prisma.post.findMany({
skip: (page - 1) * pageSize,
take: pageSize
});
// schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
// 连接池设置
directUrl = env("DIRECT_URL")
// 无服务器连接限制
relationMode = "prisma" // 用于 PlanetScale, Neon
}
# .env
DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=20"
# 启动可视化数据库浏览器
npx prisma studio
功能:
localhost:5555| 功能 | Prisma | Drizzle |
|---|---|---|
| 模式定义 | 自定义 DSL | TypeScript 代码 |
| 类型安全 | 生成类型 | 推断类型 |
| 迁移 | 内置 (migrate) | drizzle-kit |
| 查询构建器 | 流畅 API | SQL 风格构建器 |
| 关系 | 自动 | 手动连接 |
| Studio | 内置 GUI | 无 GUI |
| 包大小 | ~300kB | ~50kB |
| 原始 SQL | 支持 | 一等公民 |
| 边缘运行时 | 有限支持 | 完全支持 |
| 学习曲线 | 中等 | 较陡 |
| 最佳适用场景 | 全栈应用、快速开发、团队 | 边缘函数、SQL 专家、对包大小敏感 |
选择 Prisma 当:
选择 Drizzle 当:
PrismaClient 实例(特别是在开发中)select 减少有效负载大小@@indexdeletedAt 字段而不是硬删除prisma.$on('query') 进行日志记录❌ 创建多个 PrismaClient 实例:
// 错误 - 导致连接泄漏
function getUser() {
const prisma = new PrismaClient(); // 每次调用都创建新实例
return prisma.user.findMany();
}
// 正确 - 单例模式
const prisma = new PrismaClient();
function getUser() {
return prisma.user.findMany();
}
❌ N+1 查询:
// 错误 - 多个查询
const users = await prisma.user.findMany();
for (const user of users) {
user.posts = await prisma.post.findMany({ where: { authorId: user.id } });
}
// 正确 - 使用 include 的单次查询
const users = await prisma.user.findMany({ include: { posts: true } });
❌ 多步骤操作缺少事务:
// 错误 - 非原子性,可能留下不一致状态
await prisma.user.delete({ where: { id: userId } });
await prisma.post.deleteMany({ where: { authorId: userId } }); // 可能失败
// 正确 - 原子事务
await prisma.$transaction([
prisma.post.deleteMany({ where: { authorId: userId } }),
prisma.user.delete({ where: { id: userId } })
]);
如果出现以下情况,请停止并重新考虑:
PrismaClientfindMany 而不进行分页migrate dev(应使用 migrate deploy)使用 Prisma 时,这些技能可以增强您的工作流程:
[如果部署在您的包中,这些技能中提供完整文档]
每周安装次数
158
仓库
GitHub 星标数
18
首次出现
2026年1月23日
安全审计
安装于
gemini-cli121
opencode121
claude-code119
cursor115
codex113
github-copilot105
Modern database toolkit for TypeScript with schema-first development, auto-generated type-safe client, and powerful migration system.
npm install prisma @prisma/client
npx prisma init
# 1. Define schema
# Edit prisma/schema.prisma
# 2. Create migration
npx prisma migrate dev --name init
# 3. Generate client
npx prisma generate
# 4. Open Studio
npx prisma studio
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([authorId])
}
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// Create
const user = await prisma.user.create({
data: {
email: 'alice@example.com',
name: 'Alice',
posts: {
create: { title: 'First Post', content: 'Hello World' }
}
},
include: { posts: true }
});
// Read with filters
const users = await prisma.user.findMany({
where: { email: { contains: '@example.com' } },
include: { posts: { where: { published: true } } },
orderBy: { createdAt: 'desc' },
take: 10
});
// Update
await prisma.user.update({
where: { id: userId },
data: { name: 'Bob' }
});
// Delete
await prisma.user.delete({ where: { id: userId } });
model Product {
id Int @id @default(autoincrement())
sku String @unique
name String
description String? // Optional field
price Decimal @db.Decimal(10, 2)
inStock Boolean @default(true)
quantity Int @default(0)
tags String[] // Array field (PostgreSQL)
metadata Json? // JSON field
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([sku])
@@index([name, inStock])
}
One-to-Many:
model User {
id String @id @default(cuid())
posts Post[]
}
model Post {
id String @id @default(cuid())
author User @relation(fields: [authorId], references: [id])
authorId String
@@index([authorId])
}
Many-to-Many:
model Post {
id String @id @default(cuid())
categories Category[] @relation("PostCategories")
}
model Category {
id String @id @default(cuid())
name String @unique
posts Post[] @relation("PostCategories")
}
One-to-One:
model User {
id String @id @default(cuid())
profile Profile?
}
model Profile {
id String @id @default(cuid())
bio String
user User @relation(fields: [userId], references: [id])
userId String @unique
}
Self-Relations:
model User {
id String @id @default(cuid())
following User[] @relation("UserFollows")
followers User[] @relation("UserFollows")
}
// Create with nested relations
const user = await prisma.user.create({
data: {
email: 'bob@example.com',
profile: {
create: { bio: 'Software Engineer' }
},
posts: {
create: [
{ title: 'Post 1', content: 'Content 1' },
{ title: 'Post 2', content: 'Content 2' }
]
}
}
});
// Update with nested operations
await prisma.user.update({
where: { id: userId },
data: {
posts: {
create: { title: 'New Post' },
update: {
where: { id: postId },
data: { published: true }
},
delete: { id: oldPostId }
}
}
});
Sequential (Interactive):
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: { email: 'alice@example.com' }
});
await tx.post.create({
data: { title: 'Post', authorId: user.id }
});
// Rollback if error thrown
if (someCondition) {
throw new Error('Rollback transaction');
}
});
Batch (Parallel):
const [deletedPosts, updatedUser] = await prisma.$transaction([
prisma.post.deleteMany({ where: { published: false } }),
prisma.user.update({
where: { id: userId },
data: { name: 'Updated' }
})
]);
Aggregations:
const result = await prisma.post.aggregate({
_count: { id: true },
_avg: { views: true },
_sum: { likes: true },
_max: { createdAt: true },
where: { published: true }
});
const grouped = await prisma.post.groupBy({
by: ['authorId'],
_count: { id: true },
_avg: { views: true },
having: { views: { _avg: { gt: 100 } } }
});
Raw SQL:
// Raw query
const users = await prisma.$queryRaw<User[]>`
SELECT * FROM "User" WHERE email LIKE ${`%${search}%`}
`;
// Execute
await prisma.$executeRaw`
UPDATE "Post" SET views = views + 1 WHERE id = ${postId}
`;
# Create and apply migration
npx prisma migrate dev --name add_user_role
# Reset database (WARNING: deletes all data)
npx prisma migrate reset
# View migration status
npx prisma migrate status
# Apply pending migrations
npx prisma migrate deploy
# Generate client (in CI/CD)
npx prisma generate
# Push schema without migrations (dev only)
npx prisma db push
# Pull schema from existing database
npx prisma db pull
// lib/prisma.ts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma;
}
Server Component:
// app/users/page.tsx
import { prisma } from '@/lib/prisma';
export default async function UsersPage() {
const users = await prisma.user.findMany({
include: { posts: { take: 5 } }
});
return (
<ul>
{users.map(u => (
<li key={u.id}>{u.name} - {u.posts.length} posts</li>
))}
</ul>
);
}
Server Action:
// app/actions.ts
'use server';
import { prisma } from '@/lib/prisma';
import { revalidatePath } from 'next/cache';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const authorId = formData.get('authorId') as string;
await prisma.post.create({
data: { title, authorId }
});
revalidatePath('/posts');
}
import { PrismaClient } from '@prisma/client';
import express from 'express';
const app = express();
const prisma = new PrismaClient();
app.get('/users/:id', async (req, res) => {
const user = await prisma.user.findUnique({
where: { id: req.params.id },
include: { posts: true }
});
if (!user) return res.status(404).json({ error: 'Not found' });
res.json(user);
});
app.listen(3000);
// ❌ N+1 queries
const users = await prisma.user.findMany();
for (const user of users) {
const posts = await prisma.post.findMany({
where: { authorId: user.id }
});
}
// ✅ Single query with include
const users = await prisma.user.findMany({
include: { posts: true }
});
// ✅ Select specific fields
const users = await prisma.user.findMany({
select: { id: true, email: true, posts: { select: { title: true } } }
});
// Cursor-based (recommended for large datasets)
const posts = await prisma.post.findMany({
take: 10,
cursor: lastPostId ? { id: lastPostId } : undefined,
skip: lastPostId ? 1 : 0,
orderBy: { createdAt: 'desc' }
});
// Offset-based (simple but slower)
const posts = await prisma.post.findMany({
skip: (page - 1) * pageSize,
take: pageSize
});
// schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
// Connection pool settings
directUrl = env("DIRECT_URL")
// Serverless connection limit
relationMode = "prisma" // For PlanetScale, Neon
}
# .env
DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=20"
# Launch visual database browser
npx prisma studio
Features:
localhost:5555| Feature | Prisma | Drizzle |
|---|---|---|
| Schema Definition | Custom DSL | TypeScript code |
| Type Safety | Generated types | Inferred types |
| Migrations | Built-in (migrate) | drizzle-kit |
| Query Builder | Fluent API | SQL-like builders |
| Relations | Automatic | Manual joins |
| Studio | Built-in GUI | No GUI |
| Bundle Size | ~300kB | ~50kB |
| Raw SQL | Supported | First-class |
| Edge Runtime | Limited | Full support |
| Learning Curve |
Choose Prisma when:
Choose Drizzle when:
PrismaClient instance (especially in dev)select to reduce payload size@@index on frequently queried fieldsdeletedAt field instead of hard deletesprisma.$on('query') for logging❌ Creating multiple PrismaClient instances:
// WRONG - creates connection leak
function getUser() {
const prisma = new PrismaClient(); // New instance every call
return prisma.user.findMany();
}
// CORRECT - singleton pattern
const prisma = new PrismaClient();
function getUser() {
return prisma.user.findMany();
}
❌ N+1 queries:
// WRONG - multiple queries
const users = await prisma.user.findMany();
for (const user of users) {
user.posts = await prisma.post.findMany({ where: { authorId: user.id } });
}
// CORRECT - single query with include
const users = await prisma.user.findMany({ include: { posts: true } });
❌ Missing transaction for multi-step operations:
// WRONG - not atomic, can leave inconsistent state
await prisma.user.delete({ where: { id: userId } });
await prisma.post.deleteMany({ where: { authorId: userId } }); // May fail
// CORRECT - atomic transaction
await prisma.$transaction([
prisma.post.deleteMany({ where: { authorId: userId } }),
prisma.user.delete({ where: { id: userId } })
]);
Stop and reconsider if:
PrismaClient in request handlersfindMany without pagination on large tablesmigrate dev in production (use migrate deploy)When using Prisma, these skills enhance your workflow:
[Full documentation available in these skills if deployed in your bundle]
Weekly Installs
158
Repository
GitHub Stars
18
First Seen
Jan 23, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
gemini-cli121
opencode121
claude-code119
cursor115
codex113
github-copilot105
Graft 框架:一次定义,同时提供 HTTP 和 MCP 服务的 API 开发与代理工具
916 周安装
| Moderate |
| Steeper |
| Best For | Full-stack apps, rapid development, teams | Edge functions, SQL experts, bundle-sensitive |