Install
npx skillscat add tachfineamnay/lumirav2/database-prisma Install via the SkillsCat registry.
SKILL.md
Database & Prisma
Context
- Database: PostgreSQL 16 (Alpine)
- ORM: Prisma 5.x
- Schema:
packages/database/prisma/schema.prisma - Client: Generated to
@packages/database
Core Principles
- Single Source of Truth: The
schema.prismafile defines all models. - Type Safety: Use generated Prisma types everywhere. Never manually type DB responses.
- Migrations First: Always use migrations, never direct SQL in production.
Schema Location
packages/database/
├── prisma/
│ ├── schema.prisma # Main schema
│ ├── migrations/ # Version-controlled migrations
│ └── seed.ts # Development seeding
├── src/
│ └── index.ts # Re-exports prisma client
└── package.jsonCommands
# Generate Prisma client (after schema change)
pnpm db:generate
# Create and apply migration
pnpm db:migrate --name describe_change
# Push changes (prototyping only, no migration)
pnpm db:push
# Open Prisma Studio
pnpm db:studio
# Seed database
pnpm db:seedSchema Best Practices
Enums for Finite States
enum MissionStatus {
DRAFT
PUBLISHED
ASSIGNED
COMPLETED
CANCELLED
}Indexes for Performance
model Mission {
id String @id @default(cuid())
userId String
status MissionStatus
createdAt DateTime @default(now())
@@index([userId])
@@index([status, createdAt])
}Explicit Relation Names
model User {
sentMessages Message[] @relation("SentMessages")
receivedMessages Message[] @relation("ReceivedMessages")
}Transactions
ALWAYS use transactions for multi-step operations:
await prisma.$transaction(async (tx) => {
const mission = await tx.mission.create({ data: missionData });
await tx.notification.create({
data: { userId: mission.userId, type: 'MISSION_CREATED' }
});
return mission;
});Production Migrations
Via Coolify Deployment
- Migrations run automatically via
prisma migrate deployin Dockerfile. - Ensure
DATABASE_URLis set in Coolify environment.
Manual Migration
# SSH into server or use Coolify terminal
npx prisma migrate deployCommon Patterns
Soft Deletes
model User {
deletedAt DateTime?
}// Query active users only
const users = await prisma.user.findMany({
where: { deletedAt: null }
});Pagination
const { skip, take } = { skip: (page - 1) * limit, take: limit };
const [items, total] = await prisma.$transaction([
prisma.mission.findMany({ skip, take, orderBy: { createdAt: 'desc' } }),
prisma.mission.count()
]);