| name | prisma |
| description | Prisma ORM for TypeScript - Type-safe database toolkit with schema-first development, auto-generated client, migrations, relations, and Prisma Studio |
| user-invocable | false |
| disable-model-invocation | true |
| progressive_disclosure | {"entry_point":{"summary":"Prisma ORM for TypeScript - Type-safe database toolkit with schema-first development, auto-generated client, migrations, relations, and Prisma Studio","when_to_use":"When working with version control, branches, or pull requests.","quick_start":"1. Review the core concepts below. 2. Apply patterns to your use case. 3. Follow best practices for implementation."}} |
Prisma ORM - Type-Safe Database Toolkit
Modern database toolkit for TypeScript with schema-first development, auto-generated type-safe client, and powerful migration system.
Quick Reference
Installation
npm install prisma @prisma/client
npx prisma init
Basic Workflow
npx prisma migrate dev --name init
npx prisma generate
npx prisma studio
Core Schema Pattern
// 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])
}
Type-Safe CRUD
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' }
});
prisma..({ : { : userId } });
Schema Design Patterns
Field Types and Attributes
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])
}
Relations
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")
}
Client Operations
Nested Writes
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' }
]
}
}
});
await prisma.user.update({
where: { id: userId },
data: {
posts: {
create: { title: 'New Post' },
update: {
where: { id: postId },
data: { published: true }
},
delete: { id: oldPostId }
}
}
});
Transactions
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 }
});
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' }
})
]);
Advanced Queries
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:
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}
`;
Migrations
Development Workflow
npx prisma migrate dev --name add_user_role
npx prisma migrate reset
npx prisma migrate status
Production Deployment
npx prisma migrate deploy
npx prisma generate
Schema Prototyping
npx prisma db push
npx prisma db pull
Integration Patterns
Next.js App Router
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:
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:
'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');
}
Node.js Middleware
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);
Performance Optimization
Query Optimization
const users = await prisma.user.findMany();
for (const user of users) {
const posts = await prisma.post.findMany({
where: { authorId: user.id }
});
}
const users = await prisma.user.findMany({
include: { posts: true }
});
const users = await prisma.user.findMany({
select: { id: true, email: true, posts: { select: { title: true } } }
});
Pagination
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
});
Connection Pooling
// 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
}
DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=20"
Prisma Studio
npx prisma studio
Features:
- Visual data browser and editor
- Create, read, update, delete records
- Filter and search data
- View relations visually
- Runs on
localhost:5555
Prisma vs Drizzle
| 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 | Moderate | Steeper |
| Best For | Full-stack apps, rapid development, teams | Edge functions, SQL experts, bundle-sensitive |
Choose Prisma when:
- Team prefers schema-first development
- Need visual database tools (Studio)
- Want automatic relation handling
- Building full-stack monoliths
- Rapid prototyping and migrations
Choose Drizzle when:
- Need minimal bundle size (edge functions)
- Prefer SQL-like syntax
- Edge runtime deployment (Cloudflare Workers)
- Want full control over SQL generation
- Team has strong SQL expertise
Best Practices
- Singleton Pattern - Reuse
PrismaClient instance (especially in dev)
- Connection Management - Configure pool size for serverless
- Select Specific Fields - Use
select to reduce payload size
- Use Transactions - For multi-step operations requiring atomicity
- Index Strategically - Add
@@index on frequently queried fields
- Migration Discipline - Never edit migrations after deployment
- Schema Versioning - Use descriptive migration names
- Soft Deletes - Add
deletedAt field instead of hard deletes
- Validate Before Saving - Use Zod schemas before Prisma operations
- Monitor Queries - Use
prisma.$on('query') for logging
Common Pitfalls
❌ Creating multiple PrismaClient instances:
function getUser() {
const prisma = new PrismaClient();
return prisma.user.findMany();
}
const prisma = new PrismaClient();
function getUser() {
return prisma.user.findMany();
}
❌ N+1 queries:
const users = await prisma.user.findMany();
for (const user of users) {
user.posts = await prisma.post.findMany({ where: { authorId: user.id } });
}
const users = await prisma.user.findMany({ include: { posts: true } });
❌ Missing transaction for multi-step operations:
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 } })
]);
Red Flags
Stop and reconsider if:
- Creating new
PrismaClient in request handlers
- Not using transactions for multi-step operations
- Missing indexes on foreign keys or frequently queried fields
- Using
findMany without pagination on large tables
- Fetching entire objects when only specific fields needed
- Not handling connection errors in production
- Using
migrate dev in production (use migrate deploy)
Integration with Other Skills
- typescript-core: Zod validation, type safety patterns
- nextjs-core: Server Actions, Server Components integration
- nextjs-v16: App Router data fetching, caching
- database-migration: Safe schema evolution patterns
Resources
Related Skills
When using Prisma, these skills enhance your workflow:
- drizzle: Drizzle ORM as lightweight alternative to Prisma
- typescript: TypeScript best practices for Prisma generated types
- nextjs: Prisma with Next.js: connection pooling, edge runtime considerations
- test-driven-development: Testing Prisma models, migrations, and queries
[Full documentation available in these skills if deployed in your bundle]