db-prisma
Prisma Database Skill
Production patterns for Prisma 7 ORM with PostgreSQL, including schema design, versioned migrations, seeding, and Railway-specific configuration.
When to Use This Skill
- Setting up Prisma in a new project
- Designing database schemas (models, relations, enums)
- Writing and running migrations
- Seeding development databases
- Deploying Prisma-backed services to Railway
- Troubleshooting Prisma/PostgreSQL issues
Project Setup
Installation
npm install prisma @prisma/client
npx prisma init --datasource-provider postgresql
Directory Structure
prisma/
schema.prisma # Schema definition
migrations/ # Versioned SQL migrations
seed.ts # Database seeder
Environment
# .env (development)
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/myapp?schema=public"
# Railway (production) — auto-wired from Postgres service
DATABASE_URL="postgresql://user:pass@host:port/railway?schema=public&connection_limit=5"
Schema Patterns
Base Schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
Model Conventions
Singular model names, PascalCase. Fields use camelCase. Database columns use snake_case via @map.
model User {
id String @id @default(uuid())
email String @unique
name String
role Role @default(MEMBER)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
sessions Session[]
posts Post[]
@@map("user")
}
Timestamps
Every model gets createdAt and updatedAt:
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
Enums
enum Role {
ADMIN
MEMBER
GUEST
}
enum Status {
DRAFT
PUBLISHED
ARCHIVED
}
Relations
One-to-Many:
model User {
id String @id @default(uuid())
posts Post[]
@@map("user")
}
model Post {
id String @id @default(uuid())
title String
userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@map("post")
}
Many-to-Many (explicit join table):
model Post {
id String @id @default(uuid())
tags PostTag[]
@@map("post")
}
model Tag {
id String @id @default(uuid())
name String @unique
posts PostTag[]
@@map("tag")
}
model PostTag {
postId String @map("post_id")
tagId String @map("tag_id")
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
@@id([postId, tagId])
@@map("post_tag")
}
Indexes
model User {
id String @id @default(uuid())
email String @unique
name String
@@index([name])
@@map("user")
}
PostgreSQL-Specific Types
model Product {
id String @id @default(uuid())
metadata Json?
tags String[]
price Decimal @db.Decimal(10, 2)
@@map("product")
}
Migration Workflow
Development
# Create migration from schema changes
npx prisma migrate dev --name add_user_table
# Apply without creating (schema push for prototyping)
npx prisma db push
# Reset database (drops all data)
npx prisma migrate reset
# Check migration status
npx prisma migrate status
Production (Railway)
# Deploy pending migrations (non-interactive, no data loss prompts)
npx prisma migrate deploy
Key Rules
- Never edit existing migrations — they may have run in production
- Always review generated SQL before committing
- Use
migrate devlocally,migrate deployin CI/production - Commit the
migrations/directory — it's your migration history - Use
db pushonly for prototyping — no migration history
Client Usage
Generate Client
npx prisma generate
Run after every schema change. The build command should include this.
Basic CRUD
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// Create
const user = await prisma.user.create({
data: { email: 'user@example.com', name: 'John' },
});
// Read one
const found = await prisma.user.findUnique({
where: { email: 'user@example.com' },
});
// Read many with filter
const users = await prisma.user.findMany({
where: { role: 'MEMBER' },
orderBy: { createdAt: 'desc' },
take: 10,
});
// Update
await prisma.user.update({
where: { id: user.id },
data: { name: 'Jane' },
});
// Delete
await prisma.user.delete({ where: { id: user.id } });
Relations
// Include relations
const userWithPosts = await prisma.user.findUnique({
where: { id: userId },
include: { posts: true, sessions: true },
});
// Nested create
const user = await prisma.user.create({
data: {
email: 'user@example.com',
name: 'John',
posts: {
create: [
{ title: 'First Post' },
{ title: 'Second Post' },
],
},
},
include: { posts: true },
});
// Select specific fields
const emails = await prisma.user.findMany({
select: { id: true, email: true },
});
Transactions
const [user, post] = await prisma.$transaction([
prisma.user.create({ data: { email: 'a@b.com', name: 'A' } }),
prisma.post.create({ data: { title: 'Hello', userId: 'known-id' } }),
]);
// Interactive transaction
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: { email: 'a@b.com', name: 'A' },
});
await tx.post.create({
data: { title: 'Hello', userId: user.id },
});
});
Seeding
seed.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
await prisma.user.upsert({
where: { email: 'admin@example.com' },
update: {},
create: {
email: 'admin@example.com',
name: 'Admin',
role: 'ADMIN',
},
});
console.log('Seed complete');
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(() => prisma.$disconnect());
package.json config
{
"prisma": {
"seed": "tsx prisma/seed.ts"
}
}
Run seed
npx prisma db seed
Seed runs automatically after prisma migrate reset.
Railway Deployment
Connection String
Railway Postgres provides DATABASE_URL automatically when the backend service references the Postgres service. Append connection pooling for production:
DATABASE_URL="postgresql://user:pass@host:port/railway?schema=public&connection_limit=5"
Build Command
npx prisma generate && npx prisma migrate deploy && npm run build
This sequence:
- Generates the Prisma client
- Applies any pending migrations
- Builds the application
railway.toml
[build]
builder = "nixpacks"
buildCommand = "npx prisma generate && npx prisma migrate deploy && npm run build"
[deploy]
startCommand = "node dist/main.js"
healthcheckPath = "/health"
healthcheckTimeout = 300
restartPolicyType = "ON_FAILURE"
restartPolicyMaxRetries = 10
Prisma Studio
npx prisma studio
Opens a browser-based GUI for viewing and editing data at http://localhost:5555.
Package Scripts
{
"scripts": {
"db:generate": "prisma generate",
"db:migrate": "prisma migrate dev",
"db:migrate:deploy": "prisma migrate deploy",
"db:push": "prisma db push",
"db:reset": "prisma migrate reset",
"db:seed": "prisma db seed",
"db:studio": "prisma studio"
}
}
Troubleshooting
"Can't reach database server"
Check DATABASE_URL is set and Postgres is running:
echo $DATABASE_URL
docker compose ps # if using Docker locally
"Migration failed"
Check migration status and logs:
npx prisma migrate status
npx prisma migrate resolve --rolled-back <migration-name>
"Prisma Client not generated"
Run generate after schema changes:
npx prisma generate
Connection pool exhaustion on Railway
Append &connection_limit=5 to DATABASE_URL. Railway instances have limited connections.
Type errors after schema change
Regenerate client and restart TypeScript server:
npx prisma generate
# Restart TS server in IDE: Cmd+Shift+P → "TypeScript: Restart TS Server"
More from aussiegingersnap/cursor-skills
ui-shadcn-studio
shadcn/studio component library with MCP integration, theme generation, and block patterns. This skill should be used when building UI with shadcn components, selecting dashboard layouts, or generating landing pages. Canonical source for all shadcn-based work.
7db-postgres
PostgreSQL database management with Drizzle ORM, versioned migrations, and type-safe queries. This skill should be used when setting up a new database, writing migrations, managing schemas, or troubleshooting database issues in PostgreSQL projects.
7secrets-1password
1Password Developer CLI skill for secure secrets management. This skill should be used when creating, reading, updating, or deleting secrets in 1Password vaults, injecting secrets into script execution, or setting up 1Password CLI authentication.
7feature-build
Orchestrator skill for the complete feature development lifecycle. Coordinates 5 phases - task selection, component design, build loop, analytics setup, and commit/documentation. Use when building any new feature or enhancement that requires multiple steps.
6judge
Quality review skill for verifying complex changes against criteria. Use for multi-file changes, new features, or before important commits. Skip for trivial fixes and quick iterations.
6documentation
Maintain organized project documentation with metadata headers. Update existing docs before creating new ones. Use when writing or editing any project documentation.
6