Prisma Migrations: The Complete Workflow Guide
Prisma migrations are straightforward in development, tricky in production. Here's the complete workflow — from first migration to deploying safely to production.
Development Workflow
Create and apply a migration
# After editing schema.prisma:
npx prisma migrate dev --name add_user_subscription
This:
- Generates SQL for the schema diff
- Applies the migration to your dev database
- Regenerates the Prisma client
- Creates a migration file in
prisma/migrations/
Reset everything
npx prisma migrate reset
# Drops the database, re-runs all migrations, runs seed
Use when: you want a clean slate in development, or you changed migrations you haven't deployed yet.
Inspect current state
npx prisma migrate status
# Shows which migrations have been applied and which are pending
The Migration File Structure
prisma/
migrations/
20260401120000_init/
migration.sql
20260407093000_add_user_subscription/
migration.sql
schema.prisma
Each migration folder contains a single SQL file. These are committed to git — they're your database change history.
Production Deployment
Never run prisma migrate dev in production — it prompts interactively and can drop data. Use migrate deploy instead:
npx prisma migrate deploy
This applies any pending migrations without interactive prompts or data loss.
In your deployment pipeline:
# Run before starting the app
npx prisma migrate deploy && npm start
For Vercel, add to package.json:
{
"scripts": {
"build": "prisma generate && prisma migrate deploy && next build"
}
}
Handling Breaking Changes
Dropping a column or renaming a table requires care in production.
The safe pattern for renaming a column:
Step 1: Add the new column (backward compatible)
model User {
fullName String? // old
displayName String? // new
}
Step 2: Deploy code that writes to both columns, reads from new
Step 3: Backfill data
UPDATE "User" SET "displayName" = "fullName" WHERE "displayName" IS NULL;
Step 4: Remove old column in a separate migration after verifying
Never: rename and deploy in a single migration — you'll get errors from in-flight requests that still use the old column name.
Seeding the Database
prisma/seed.ts:
import { PrismaClient } from '@prisma/client';
const db = new PrismaClient();
async function main() {
await db.user.upsert({
where: { email: 'admin@example.com' },
update: {},
create: {
email: 'admin@example.com',
name: 'Admin',
},
});
}
main()
.catch(console.error)
.finally(() => db.$disconnect());
package.json:
{
"prisma": {
"seed": "tsx prisma/seed.ts"
}
}
Run: npx prisma db seed
Common Issues
P3009: migrate found failed migration
A migration was partially applied and left in a failed state. Fix:
npx prisma migrate resolve --rolled-back 20260407093000_add_user_subscription
# Then fix the migration and re-deploy
Database is not up to date
The schema doesn't match the database. Check:
npx prisma migrate status
Migrations drift (schema and files disagree)
npx prisma migrate diff \
--from-migrations ./prisma/migrations \
--to-schema-datamodel ./prisma/schema.prisma \
--shadow-database-url $DATABASE_URL
Pre-Configured in the Starter Kit
The AI SaaS Starter Kit includes the Prisma schema, initial migration, seed file with test data, and deployment configuration.
Atlas — building at whoffagents.com
Top comments (0)