Prisma + PostgreSQL Schema Skill
Skill for analyzing, building, and structuring Prisma schemas targeting PostgreSQL. Covers model design, relation mapping, migration generation, and type output.
Workflow
1. Identify the task type
| Task | Description |
|---|---|
| Analyze | User provides existing schema.prisma — review structure, spot issues |
| Build | User describes what they need — generate Prisma models from scratch |
| Structure | User provides raw fields or SQL DDL — convert and normalize to Prisma |
| Migrate | Diff between old and new schema — generate safe migration |
| Type Models | Generate TypeScript types / Zod schemas from Prisma models |
| Relations | Design or fix 1-1, 1-N, N-M relationships in Prisma |
2. Prisma file header (always include)
Every schema.prisma starts with:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
3. Analyze mode
When given an existing schema.prisma:
- Check for missing
@@indexon relation fields (FK columns) - Flag models missing
createdAt/updatedAt - Verify naming conventions (PascalCase models, camelCase fields)
- Check relation completeness (both sides of
@relationdefined) - Spot missing
onDelete/onUpdatepolicies - Flag
Stringfields that should beenum - Check UUID vs auto-increment consistency
- Detect missing
@@uniqueconstraints where applicable - Flag optional fields (
?) that should be required
Output format: structured audit report per issue:
- Issue: What's wrong
- Location:
Model.field - Fix: Suggested Prisma SDL
4. Build mode
When generating models from scratch:
Standard base fields for every model:
model Example {
id String @id @default(uuid()) @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Naming conventions:
- Models: PascalCase, singular (
User,WalletTransaction) - Fields: camelCase (
walletAddress,isActive) - Enums: PascalCase, values in SCREAMING_SNAKE_CASE
- Relation fields: camelCase, named after the related model
Index rules:
- Always add
@@indexon relation fields (userId,walletId, etc.) - Add
@@indexon fields used inwhere,orderBy, orincludefrequently - Use
@@uniquefor natural unique keys (email, slug, etc.)
Enum pattern:
enum UserRole {
ADMIN
USER
MODERATOR
}
model User {
role UserRole @default(USER)
}
5. Relations
Read /references/relations.md for full patterns on each relation type.
Quick reference:
1-to-many:
model User {
id String @id @default(uuid()) @db.Uuid
posts Post[]
}
model Post {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
}
Many-to-many (explicit join table — preferred):
model Post {
id String @id @default(uuid()) @db.Uuid
tags PostToTag[]
}
model Tag {
id String @id @default(uuid()) @db.Uuid
posts PostToTag[]
}
model PostToTag {
postId String @db.Uuid
tagId String @db.Uuid
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
@@id([postId, tagId])
@@index([tagId])
}
onDelete policies:
| Policy | Use when |
|---|---|
Cascade |
Child should be deleted with parent |
Restrict |
Block parent delete if children exist |
SetNull |
Nullify FK on parent delete (field must be optional) |
NoAction |
DB-level default |
6. Structure / Convert mode
When converting SQL DDL → Prisma:
- Map SQL types → Prisma types (see
/references/type-mappings.md) - Convert
FOREIGN KEY→@relationwith properfields/references - Convert
UNIQUEconstraints →@@unique - Convert
INDEX→@@index - Replace
SERIAL→@default(autoincrement()) - Replace
gen_random_uuid()→@default(uuid()) - Add
@db.*type hints when precision/type matters
7. Migrate mode
When the user wants to generate or apply migrations:
Step 1 — Classify the risk:
| Risk | Examples |
|---|---|
| LOW | New model, new optional field, new index |
| MEDIUM | Adding required field with default, renaming |
| HIGH | Removing field/model, changing type, required field without default |
Step 2 — Generate migration SQL for review:
-- Migration: <short description>
-- Prisma equivalent: prisma migrate dev --name <migration_name>
-- Risk: LOW / MEDIUM / HIGH
BEGIN;
ALTER TABLE "users" ADD COLUMN "display_name" TEXT;
CREATE INDEX CONCURRENTLY "idx_users_display_name" ON "users"("display_name");
COMMIT;
Step 3 — Confirm with the user before executing (especially MEDIUM or HIGH risk).
Step 4 — Delegate execution to the prisma-migrator agent:
Agent(
subagent_type: "prisma-migrator",
prompt: "Run: npx prisma migrate dev --name <migration_name>. Schema is at prisma/schema.prisma."
)
8. Type Models mode
Generate TypeScript types or Zod schemas from Prisma models on request.
Read /references/type-mappings.md for full type mapping table.
Zod example from Prisma model:
import { z } from 'zod'
export const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
role: z.enum(['ADMIN', 'USER', 'MODERATOR']),
createdAt: z.date(),
updatedAt: z.date(),
})
export type User = z.infer<typeof UserSchema>
Output rules
- Always output valid Prisma SDL — no pseudocode
- Include the full
schema.prismaheader when building from scratch - Add
// reason: ...comments for non-obvious decisions - When building from scratch: show full schema first, then briefly explain key decisions
- For audits: one issue per block, direct and concise
- Never omit
@@indexon relation fields — it's a hard rule
Reference files
references/type-mappings.md— PostgreSQL / TypeScript / Zod ↔ Prisma type mappingsreferences/relations.md— Full patterns for 1-1, 1-N, N-M, self-relationsreferences/common-patterns.md— Soft delete, multi-tenancy, audit log, pagination