Prisma Core Knowledge
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: prisma for comprehensive documentation.
When NOT to Use This Skill
- Raw SQL Operations: Use the
database-query MCP server for direct SQL queries and database inspection
- Other ORMs: Use appropriate skills (
drizzle, typeorm, sqlalchemy) for other ORM frameworks
- Database Design: Consult
architect-expert or sql-expert for database architecture decisions
- Performance Profiling: Use
performance-profiler MCP for query performance analysis
- Migration Rollbacks: Engage
devops-expert for production migration strategies
Schema Definition
// schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}
CRUD Operations
// Create
const user = await prisma.user.create({
data: { email: 'user@example.com', name: 'John' }
});
// Read
const users = await prisma.user.findMany({
where: { email: { contains: '@example.com' } },
include: { posts: true },
orderBy: { createdAt: 'desc' },
take: 10
});
const user = await prisma.user.findUnique({
where: { id: 1 }
});
// Update
await prisma.user.update({
where: { id: 1 },
data: { name: 'Jane' }
});
// Delete
await prisma.user.delete({ where: { id: 1 } });
Relations
// Create with relation
await prisma.user.create({
data: {
email: 'user@example.com',
posts: {
create: [
{ title: 'First Post' },
{ title: 'Second Post' }
]
}
}
});
// Query with relation
const userWithPosts = await prisma.user.findUnique({
where: { id: 1 },
include: { posts: { where: { published: true } } }
});
Commands
npx prisma init
npx prisma migrate dev --name init
npx prisma generate
npx prisma studio
Anti-Patterns
| Anti-Pattern |
Why It's Bad |
Better Approach |
synchronize: true in production |
Can cause data loss, no migration history |
Use prisma migrate deploy |
No select or include on queries |
Fetches all fields, performance waste |
Select only needed fields |
N+1 queries without include |
Multiple round trips to database |
Use include or select with nested relations |
| Missing indexes on foreign keys |
Slow joins and lookups |
Add @@index([foreignKeyField]) |
| Hardcoded connection strings |
Security risk, no environment flexibility |
Use env("DATABASE_URL") |
| No transaction for multi-step operations |
Data inconsistency risk |
Wrap in $transaction |
Using findMany() without pagination |
Memory issues with large datasets |
Add take and skip or cursor-based pagination |
| Ignoring Prisma error codes |
Poor user experience, security risks |
Handle PrismaClientKnownRequestError codes |
| No connection pool limits |
Connection exhaustion |
Configure connection_limit in URL |
| Running migrations manually in prod |
Human error, no audit trail |
Automate via CI/CD with migrate deploy |
Quick Troubleshooting
| Issue |
Likely Cause |
Solution |
| "Can't reach database server" |
Wrong DATABASE_URL or DB not running |
Verify connection string, check DB status |
| "Unique constraint failed" (P2002) |
Duplicate value in unique field |
Check for existing record, handle error gracefully |
| "Foreign key constraint failed" (P2003) |
Referenced record doesn't exist |
Verify related record exists before insertion |
| "Record not found" (P2025) |
Query returned no results |
Use findUnique with null check or handle error |
| Slow queries |
Missing indexes, N+1 problem |
Add indexes, use include for relations |
| "Type 'X' is not assignable" |
Generated client out of sync |
Run npx prisma generate |
| Migration conflicts |
Multiple developers creating migrations |
Coordinate migrations, use git properly |
| Connection pool exhausted |
Too many concurrent connections |
Increase connection_limit or use Prisma Accelerate |
| "Client is not connected" |
PrismaClient not initialized |
Ensure new PrismaClient() before use |
| Schema drift |
Manual DB changes not in schema |
Use prisma db pull to sync or create migration |
Production Readiness
Security Configuration
// schema.prisma - Secure connection
datasource db {
provider = "postgresql"
url = env("DATABASE_URL") // Use env vars, never hardcode
}
// DATABASE_URL format with SSL
// postgresql://user:pass@host:5432/db?sslmode=require&sslcert=/path/to/client-cert.pem
// Secure PrismaClient initialization
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient({
log: process.env.NODE_ENV === 'production'
? ['error']
: ['query', 'info', 'warn', 'error'],
errorFormat: process.env.NODE_ENV === 'production' ? 'minimal' : 'pretty',
});
Connection Pooling
// schema.prisma - Connection pool settings
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
// For serverless (e.g., Vercel, AWS Lambda)
// Use Prisma Accelerate or PgBouncer
}
// Connection management
const prisma = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL,
},
},
});
// Connection pool via URL params
// ?connection_limit=5&pool_timeout=10
// For serverless: use Prisma Accelerate
// DATABASE_URL="prisma://accelerate.prisma-data.net/?api_key=xxx"
Query Optimization
// Select only needed fields
const users = await prisma.user.findMany({
select: {
id: true,
email: true,
// Don't select unnecessary fields
},
});
// Use pagination
const users = await prisma.user.findMany({
take: 20,
skip: (page - 1) * 20,
cursor: lastId ? { id: lastId } : undefined,
});
// Batch operations for bulk inserts
await prisma.user.createMany({
data: users,
skipDuplicates: true,
});
// Use transactions for related operations
await prisma.$transaction([
prisma.order.create({ data: orderData }),
prisma.inventory.update({ where: { productId }, data: { quantity: { decrement: 1 } } }),
]);
// Interactive transactions with timeout
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({ data: userData });
await tx.profile.create({ data: { userId: user.id, ...profileData } });
return user;
}, {
maxWait: 5000, // Wait for connection
timeout: 10000, // Transaction timeout
});
Error Handling
import { Prisma } from '@prisma/client';
async function createUser(data: UserInput) {
try {
return await prisma.user.create({ data });
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
// Unique constraint violation
if (error.code === 'P2002') {
throw new ConflictError(`User with this ${error.meta?.target} already exists`);
}
// Foreign key constraint violation
if (error.code === 'P2003') {
throw new BadRequestError('Related record not found');
}
// Record not found
if (error.code === 'P2025') {
throw new NotFoundError('Record not found');
}
}
throw error;
}
}
Migrations in Production
# Generate migration (development)
npx prisma migrate dev --name add_user_status
# Apply migrations (production)
npx prisma migrate deploy
# Reset database (DANGER - only for development)
npx prisma migrate reset
# Check migration status
npx prisma migrate status
# CI/CD migration workflow
jobs:
migrate:
steps:
- name: Apply migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
Monitoring Metrics
| Metric |
Alert Threshold |
| Query duration p99 |
> 100ms |
| Connection pool wait time |
> 5s |
| Failed queries |
> 1% |
| Connection pool exhaustion |
Any occurrence |
| Slow queries count |
> 10/min |
Logging & Tracing
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient({
log: [
{ level: 'query', emit: 'event' },
{ level: 'error', emit: 'stdout' },
],
});
// Log slow queries
prisma.$on('query', (e) => {
if (e.duration > 100) {
console.log(`Slow query (${e.duration}ms): ${e.query}`);
}
});
// OpenTelemetry integration
import { PrismaInstrumentation } from '@prisma/instrumentation';
registerInstrumentations({
instrumentations: [new PrismaInstrumentation()],
});
Graceful Shutdown
// Proper cleanup on shutdown
async function gracefulShutdown() {
await prisma.$disconnect();
process.exit(0);
}
process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);
Checklist
Reference Documentation
- Schema Reference
- Query Patterns
- Deep: Relations