| name | wasp-database |
| description | Complete database migration workflow and schema management for Wasp projects. Use when modifying schema.prisma, running migrations, or working with database models. Includes MANDATORY restart requirement and PostgreSQL setup. |
| triggers | migration, schema change, database, prisma, migrate, schema.prisma, database model, postgres, db migrate, entity, wasp db |
| version | 1 |
| last_updated | Sat Oct 18 2025 00:00:00 GMT+0000 (Coordinated Universal Time) |
| allowed_tools | Edit, Bash, Read |
Wasp Database Migration Skill
Quick Reference
When to use this skill:
- Modifying database schema
- Adding/changing models or fields
- Running migrations
- Database setup and configuration
- Schema errors or type mismatches
Key concepts:
- ALWAYS use
wasp db migrate-dev(NEVERprisma migrate dev) - MANDATORY restart after migration for types to update
- PostgreSQL required for enums and PgBoss jobs
- Migration files are immutable (never edit/delete)
Critical Rules
NEVER use: npx prisma migrate dev (WRONG)
ALWAYS use: wasp db migrate-dev (CORRECT)
MANDATORY: Restart ../scripts/safe-start.sh after migration (multi-worktree safe, types regenerate)
NO exceptions: Even "simple" changes require restart - types will be stale without it
Complete Migration Workflow
Step 1: Edit schema.prisma
Location: app/schema.prisma
Common schema patterns:
// Primary key with auto-increment
model Task {
id Int @id @default(autoincrement())
description String
isDone Boolean @default(false)
createdAt DateTime @default(now())
// Relations
user User @relation(fields: [userId], references: [id])
userId Int
}
// Optional fields (add ?)
model User {
id Int @id @default(autoincrement())
email String? @unique // Optional
username String @unique // Required
}
// Enums (requires PostgreSQL)
enum TaskStatus {
TODO
IN_PROGRESS
DONE
}
model Task {
status TaskStatus @default(TODO)
}
// Unique constraints
model User {
email String @unique
@@unique([organizationId, username]) // Composite unique
}
Common field types:
String- TextInt- IntegerBoolean- True/falseDateTime- TimestampJson- JSON dataString?- Optional string- Add
@uniquefor unique constraint - Add
@default(...)for default value
Step 2: Run Migration
wasp db migrate-dev --name "Descriptive migration name"
Examples of good migration names:
- ✅ "Add email to User"
- ✅ "Create Task model"
- ✅ "Add status field to Task"
- ✅ "Add unique constraint to username"
- ❌ "Update" (too vague)
- ❌ "Changes" (not descriptive)
- ❌ "Migration" (meaningless)
What this command does:
- Generates SQL migration file in
app/migrations/ - Applies migration to database
- Updates Prisma client
- DOES NOT regenerate Wasp types (requires restart!)
Command output:
Applying migration `20251018120000_add_email_to_user`
✓ Generated Prisma Client to ./node_modules/@prisma/client
Step 3: RESTART Wasp (MANDATORY)
# Stop current wasp (Ctrl+C), then safe-start (multi-worktree safe)
../scripts/safe-start.sh
Why restart is MANDATORY:
- Wasp types only regenerate on restart
- TypeScript won't see new fields without restart
- NO exceptions - ALWAYS restart after migration
- Even for "small" changes like adding one field
Common error if you skip restart:
Property 'email' does not exist on type 'User'
Fix: Stop wasp (Ctrl+C) and run ../scripts/safe-start.sh (multi-worktree safe)
Step 4: Verify Types Updated
Check that new fields appear in TypeScript imports:
import type { Task } from "wasp/entities";
// Task type now includes new fields
const task: Task = {
id: 1,
description: "Test",
status: "TODO", // New field visible after restart
createdAt: new Date(),
};
If fields still missing:
- Confirm migration ran successfully
- Verify you restarted wasp
- Check for TypeScript cache issues:
wasp clean && ../scripts/safe-start.sh
Database Configuration
schema.prisma Structure
Required structure:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql" // or "sqlite" for development
url = env("DATABASE_URL") // ✅ MUST use env()
}
// Your models below
model User {
id Int @id @default(autoincrement())
}
Critical rules:
- ✅ Use
env("DATABASE_URL")for database URL - ❌ NEVER hardcode database URLs
- ✅ Keep schema.prisma in
app/schema.prisma - ❌ DO NOT configure database in main.wasp
PostgreSQL vs SQLite
PostgreSQL (Production & Recommended):
- ✅ Supports Prisma enums
- ✅ Supports PgBoss (background jobs)
- ✅ Production-ready
- ✅ Better performance
- ❌ Requires PostgreSQL server
SQLite (Development Only):
- ✅ No setup required
- ✅ Quick for prototyping
- ❌ NO enum support
- ❌ NO PgBoss support
- ❌ Limited concurrent writes
Recommendation: Use PostgreSQL from the start if you need enums or jobs.
Switch from SQLite to PostgreSQL:
// Before (SQLite)
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
// After (PostgreSQL)
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
Then update .env.server:
DATABASE_URL="postgresql://username:password@localhost:5432/myapp_dev"
Common Migration Errors
Error: "Cannot find module 'wasp/entities'"
Cause: Forgot to restart wasp after migration
Fix:
# Stop wasp (Ctrl+C), then safe-start (multi-worktree safe)
../scripts/safe-start.sh
Error: "Property 'newField' does not exist on type 'User'"
Cause: Types not regenerated (forgot restart)
Fix: Restart ../scripts/safe-start.sh (multi-worktree safe)
Verification:
import type { User } from "wasp/entities";
// Check if type includes new field in IDE autocomplete
Error: "Migration failed: relation already exists"
Cause: Database out of sync with migrations
Fix (Development only!):
# WARNING: This deletes all data!
wasp db reset
wasp db migrate-dev "Fresh start"
../scripts/safe-start.sh # Multi-worktree safe
Production fix: Never use db reset in production. Contact database admin.
Error: "Enum types are not supported by sqlite"
Cause: Using enums with SQLite
Fix: Switch to PostgreSQL:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
Update .env.server:
DATABASE_URL="postgresql://localhost:5432/myapp_dev"
Error: "Environment variable not found: DATABASE_URL"
Cause: Missing .env.server file
Fix: Create app/.env.server:
DATABASE_URL="postgresql://localhost:5432/myapp_dev"
Best Practices
✅ DO:
- Use descriptive migration names
- Commit migration files to git
- Restart immediately after migration
- Use
wasp db migrate-devcommand - Use
env("DATABASE_URL")in schema.prisma - Test migrations in development first
- Keep schema.prisma in app root
❌ NEVER:
- Delete migration files (breaks history)
- Edit existing migrations (create new one instead)
- Use
prisma migrate devdirectly - Skip restart after migration
- Hardcode database URLs
- Force migrations in production
- Use
wasp db resetin production
Migration Workflow Examples
Example 1: Add Field to Existing Model
Scenario: Add email field to User model
Step 1: Edit schema.prisma
// Before
model User {
id Int @id @default(autoincrement())
}
// After
model User {
id Int @id @default(autoincrement())
email String? @unique
}
Step 2: Run migration
wasp db migrate-dev "Add email to User"
Step 3: Restart wasp
# Ctrl+C to stop, then safe-start (multi-worktree safe)
../scripts/safe-start.sh
Step 4: Verify in code
import type { User } from "wasp/entities";
// email field now available on User type
Example 2: Create New Model
Scenario: Create Task model with relations
Step 1: Edit schema.prisma
model Task {
id Int @id @default(autoincrement())
description String
isDone Boolean @default(false)
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
userId Int
}
model User {
id Int @id @default(autoincrement())
tasks Task[] // Add back-relation
}
Step 2: Run migration
wasp db migrate-dev "Create Task model"
Step 3: Restart wasp
Example 3: Add Relation Between Models
Scenario: Add User-Task relation
Step 1: Edit schema.prisma
model User {
id Int @id @default(autoincrement())
tasks Task[] // Add this line
}
model Task {
id Int @id @default(autoincrement())
description String
user User @relation(fields: [userId], references: [id])
userId Int
}
Step 2: Run migration
wasp db migrate-dev "Add User-Task relation"
Step 3: Restart wasp
Example 4: Add Enum (PostgreSQL only)
Scenario: Add TaskStatus enum
Step 1: Verify PostgreSQL
datasource db {
provider = "postgresql" // Required for enums
}
Step 2: Add enum to schema.prisma
enum TaskStatus {
TODO
IN_PROGRESS
DONE
}
model Task {
id Int @id @default(autoincrement())
status TaskStatus @default(TODO)
}
Step 3: Run migration
wasp db migrate-dev "Add TaskStatus enum"
Step 4: Restart wasp
Step 5: Use in code
import type { TaskStatus } from "wasp/entities"; // Type import
import { TaskStatus } from "@prisma/client"; // Value import
// Usage
const status: TaskStatus = TaskStatus.TODO;
PostgreSQL Setup
Local Development (macOS)
# Install PostgreSQL via Homebrew
brew install postgresql
brew services start postgresql
# Create database
createdb myapp_dev
Local Development (Linux)
# Install PostgreSQL
sudo apt-get update
sudo apt-get install postgresql postgresql-contrib
# Start service
sudo service postgresql start
# Create database
sudo -u postgres createdb myapp_dev
.env.server Configuration
Create/update: app/.env.server
DATABASE_URL="postgresql://username:password@localhost:5432/myapp_dev"
Format breakdown:
postgresql://- Protocolusername:password- Database credentialslocalhost:5432- Host and portmyapp_dev- Database name
Example for local development:
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/myapp_dev"
Docker Alternative
Start PostgreSQL in Docker:
docker run --name postgres \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
-d postgres
Create database:
docker exec -it postgres psql -U postgres -c "CREATE DATABASE myapp_dev;"
.env.server:
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/myapp_dev"
Troubleshooting
Database connection failed
Check 1: PostgreSQL running
pg_isready
# Expected: localhost:5432 - accepting connections
Check 2: DATABASE_URL in .env.server
cat app/.env.server
# Verify DATABASE_URL is set
Check 3: Database exists
psql $DATABASE_URL -c "SELECT 1;"
# Should connect successfully
Migrations out of sync
Development only - reset database:
# WARNING: Deletes all data!
wasp db reset
wasp db migrate-dev "Fresh start"
../scripts/safe-start.sh # Multi-worktree safe
Production - manual sync:
- Never use
db reset - Contact database admin
- Apply migrations manually if needed
Types not showing new fields
Always the same fix: Restart ../scripts/safe-start.sh (multi-worktree safe)
If still broken:
wasp clean
../scripts/safe-start.sh # Multi-worktree safe
Enum not working
Check 1: PostgreSQL?
datasource db {
provider = "postgresql" // NOT sqlite
}
Check 2: Import values correctly?
// ✅ CORRECT - Runtime values
import { TaskStatus } from "@prisma/client";
const status = TaskStatus.TODO;
// ✅ CORRECT - Type annotation
import type { TaskStatus } from "wasp/entities";
const status: TaskStatus = "TODO";
// ❌ WRONG - Runtime values from wasp/entities
import { TaskStatus } from "wasp/entities"; // Type only, not values!
Quick Checklist
Migration workflow:
- Edit schema.prisma
- Run
wasp db migrate-dev "Description" - Wait for completion
- RESTART
../scripts/safe-start.sh(Ctrl+C, then safe-start - multi-worktree safe) - Verify types updated in TypeScript
Database setup:
- PostgreSQL installed and running (for enums/jobs)
- DATABASE_URL set in .env.server
- Database created
- Connection tested
After changes:
- Migration file committed to git
- Types visible in IDE autocomplete
- No TypeScript errors
- Application runs without errors
Critical Commands
# Run migration (CORRECT)
wasp db migrate-dev "Description"
# Reset database (dev only - DELETES DATA!)
wasp db reset
# Open Prisma Studio (database GUI)
wasp db studio
# Check migration status
wasp db migrate status
References
Complete guide:
docs/TROUBLESHOOTING-GUIDE.md(Database Issues section).tmp/extraction/wave1-agent-a-cursor-rules.md(Migration workflow)
Related skills:
wasp-operations- For using entities in operationstdd-workflow- For testing database changes
External docs:
- Prisma Schema: https://www.prisma.io/docs/concepts/components/prisma-schema
- Wasp Database: https://wasp.sh/docs/data-model/entities