| name | prisma |
| description | Prisma schema conventions and migration workflow |
Source Cursor rule: .cursor/rules/prisma.mdc.
Original file scope: **/*.prisma.
Original Cursor alwaysApply: false.
Prisma Schema
Migration Workflow
Schema changes happen in packages/db, then regenerate types in each app.
Step 1: Edit Schema
packages/db/prisma/schema/
├── schema.prisma
├── user.prisma
├── task.prisma
└── ...
Step 2: Create Migration
cd packages/db
bunx prisma migrate dev --name your_migration_name
Step 3: Regenerate Types in Apps
bun run -F apps/app db:generate
bun run -F apps/api db:generate
bun run -F apps/portal db:generate
bun run prisma:generate
✅ Always Do This
cd packages/db && bunx prisma migrate dev --name add_user_role
bun run -F apps/app db:generate
bun run -F apps/api db:generate
bun run -F apps/portal db:generate
❌ Never Do This
apps/app/prisma/schema.prisma
bunx prisma migrate dev
Core Rule
Always use prefixed CUIDs for IDs using generate_prefixed_cuid.
ID Pattern
✅ Always Do This
model User {
id String @id @default(dbgenerated("generate_prefixed_cuid('usr'::text)"))
// ... other fields
}
model Task {
id String @id @default(dbgenerated("generate_prefixed_cuid('tsk'::text)"))
// ... other fields
}
model Organization {
id String @id @default(dbgenerated("generate_prefixed_cuid('org'::text)"))
// ... other fields
}
❌ Never Do This
// Don't use UUID
model User {
id String @id @default(uuid())
}
// Don't use auto-increment
model User {
id Int @id @default(autoincrement())
}
// Don't forget ::text cast
model User {
id String @id @default(dbgenerated("generate_prefixed_cuid('usr')")) // ❌ Missing ::text
}
Prefix Guidelines
| Entity | Prefix | Example ID |
|---|
| User | usr | usr_BJRIZLgRPuWt8MvMjkSY82f1 |
| Organization | org | org_cK9xMnPqRs2tUvWx3yZa4b5c |
| Task | tsk | tsk_dE6fGhIj7kLmNoP8qRsT9uVw |
| Control | ctl | ctl_xY0zAaBb1cDdEe2fFgGh3iIj |
| Policy | pol | pol_kK4lLmMn5oOpPq6rRsSt7uUv |
Rules
- Short prefixes - Use 2-3 characters
- Unique prefixes - Each model gets its own prefix
- Always cast - Include
::text in the function call
- Use dbgenerated - Wrap the function call in
dbgenerated()
Benefits
- Human-readable IDs at a glance (
usr_ vs org_)
- Easy debugging in logs
- Safe to expose in URLs
- Unique across all tables
Checklist
After schema changes: