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
# Schema files are in packages/db/prisma/schema/
packages/db/prisma/schema/
├── schema.prisma # Main schema with datasource
├── user.prisma # User models
├── task.prisma # Task models
└── ...
Step 2: Create Migration
# Run from packages/db
cd packages/db
bunx prisma migrate dev --name your_migration_name
Step 3: Regenerate Types in Apps
# Each app needs to regenerate Prisma client types
bun run -F apps/app db:generate
bun run -F apps/api db:generate
bun run -F apps/portal db:generate
# Or from root (if configured)
bun run prisma:generate
✅ Always Do This
# 1. Make schema changes in packages/db
# 2. Create migration
cd packages/db && bunx prisma migrate dev --name add_user_role
# 3. Regenerate types in ALL apps that use the db
bun run -F apps/app db:generate
bun run -F apps/api db:generate
bun run -F apps/portal db:generate
❌ Never Do This
# Don't edit schema in app directories
apps/app/prisma/schema.prisma # ❌ Wrong location
# Don't forget to regenerate types
bunx prisma migrate dev # ✅ Created migration
# ... forgot to run db:generate in apps # ❌ Types out of sync
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
::textin the function call - Use dbgenerated - Wrap the function call in
dbgenerated()
Benefits
- Human-readable IDs at a glance (
usr_vsorg_) - Easy debugging in logs
- Safe to expose in URLs
- Unique across all tables
Checklist
After schema changes:
- Schema edited in
packages/db/prisma/schema/ - Migration created with
bunx prisma migrate dev - Types regenerated in
apps/appwithdb:generate - Types regenerated in
apps/apiwithdb:generate - Types regenerated in
apps/portalwithdb:generate - New models use prefixed CUID IDs