.claude/skills/prisma-expert/SKILL.md
Provides Prisma ORM patterns for schema design, migrations, query optimization, and relation modeling. Use when working with Prisma schema files (schema.prisma) or when the user mentions Prisma, Prisma migrations, or Prisma queries.
npx skillsauth add tranhieutt/software_development_department prisma-expertInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
migrate dev in production — use migrate deploy; migrate dev can reset dataPrismaClient() per request exhausts connections; use global singletoninclude vs select: include: { posts: true } fetches ALL post fields; use select to limit$queryRaw returns unknown[] — you must cast with as or validate; Prisma can't infer raw query types@relation on both sides causes "The relation is not defined on both sides" runtime errormodel User {
id String @id @default(cuid())
email String @unique
role Role @default(USER)
posts Post[] @relation("UserPosts")
profile Profile? @relation("UserProfile")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email])
@@map("users")
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
author User @relation("UserPosts", fields: [authorId], references: [id], onDelete: Cascade)
authorId String
@@index([authorId])
@@map("posts")
}
enum Role { USER ADMIN MODERATOR }
// ❌ N+1
const users = await prisma.user.findMany();
for (const user of users) {
const posts = await prisma.post.findMany({ where: { authorId: user.id } });
}
// ✅ Include (fetches all post fields)
const users = await prisma.user.findMany({ include: { posts: true } });
// ✅✅ Select (fetch only needed fields — best for performance)
const users = await prisma.user.findMany({
select: {
id: true, email: true,
posts: { select: { id: true, title: true } },
},
});
// ✅ Complex aggregations → use raw
const result = await prisma.$queryRaw<{ id: string; count: number }[]>`
SELECT u.id, COUNT(p.id)::int as count
FROM users u LEFT JOIN posts p ON p.author_id = u.id
GROUP BY u.id
`;
// Sequential (auto-atomic, no rollback control)
const [user, profile] = await prisma.$transaction([
prisma.user.create({ data: userData }),
prisma.profile.create({ data: profileData }),
]);
// Interactive (full control, rollback on throw)
const result = await prisma.$transaction(async (tx) => {
const user = await tx.user.create({ data: userData });
if (user.email.endsWith("@blocked.com")) throw new Error("Blocked domain");
return tx.profile.create({ data: { ...profileData, userId: user.id } });
}, {
maxWait: 5000,
timeout: 10000,
isolationLevel: "Serializable", // use ReadCommitted for most cases
});
// lib/prisma.ts
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const prisma = globalForPrisma.prisma ?? new PrismaClient({
log: process.env.NODE_ENV === "development" ? ["query"] : [],
});
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
# Development: creates migration file + applies
npx prisma migrate dev --name add_role_to_users
# Production: apply pending migrations only (safe)
npx prisma migrate deploy
# Check migration status
npx prisma migrate status
# Fix stuck migration
npx prisma migrate resolve --applied "20240115_migration_name"
# Validate schema (no DB connection needed)
npx prisma validate
# Format schema
npx prisma format
| Pitfall | Fix |
|---|---|
| migrate dev in production | Use migrate deploy |
| New PrismaClient() per request | Use global singleton |
| include: { all: true } on large models | Use select to fetch only needed fields |
| P2034 (transaction conflict) | Retry with exponential backoff |
| Shadow database error in dev | Set shadowDatabaseUrl or use Neon branching |
| Enum not syncing | Run migrate dev after enum changes |
testing
Generates high-fidelity architecture diagrams, sequence flows, and component maps for SDD projects. Use when finalizing a design phase, documenting system architecture, or visualizing agentic workflows. Default style: Style 6 (Claude Official).
data-ai
Provides vector database and semantic search patterns for Pinecone, Weaviate, Qdrant, Milvus, and pgvector in RAG and recommendation systems. Use when implementing vector search or when the user mentions vector database, semantic search, embeddings, or similarity search.
development
Updates docs/technical/CODEMAP.md by scanning the current codebase structure. Run after a significant feature merge, refactor, or when CODEMAP feels stale.
development
Unlocks the codebase after a release freeze or incident freeze period to resume normal development. Use when a freeze period ends or when the user mentions unfreezing or lifting the code freeze.