Skip to content

Prisma Migrate — Complete Guide

DodaTech Updated 2026-06-28 8 min read

In this tutorial, you will learn about Prisma Migrate. We cover key concepts, practical examples, and best practices to help you master this topic.

Learn Prisma Migrate: define declarative schema, auto-generate migrations, apply and rollback, seed databases, integrate with Node.js, and best practices for Prisma schema evolution in TypeScript applications.

What You Learn

You will learn how to use Prisma Migrate for database migrations: define a declarative schema in schema.prisma, auto-generate migrations from schema changes, apply and rollback migrations, seed databases, and integrate Prisma with Node.js applications.

Why It Matters

Prisma offers a unique declarative approach. You define the desired schema in a single file. Prisma generates migrations to get there. This is simpler than writing Migration files for every change. Prisma Migrate is the standard for modern TypeScript backends.

Real-World Use

DodaTech's TypeScript API uses Prisma with PostgreSQL. The schema.prisma file defines all 30+ models. Prisma Migrate generates migrations from schema changes. The Prisma Client provides type-safe database access. Schema changes are reviewed as PRs to schema.prisma.

Schema Definition

// schema.prisma - Declarative schema
generator client {
    provider = "prisma-client-js"
}

datasource db {
    provider = "postgresql"
    url      = env("DATABASE_URL")
}

model User {
    id        Int      @id @default(autoincrement())
    email     String   @unique
    name      String?
    phone     String?
    isActive  Boolean  @default(true) @map("is_active")
    orders    Order[]
    createdAt DateTime @default(now()) @map("created_at")
    updatedAt DateTime @updatedAt @map("updated_at")

    @@map("users")
}

model Order {
    id         Int      @id @default(autoincrement())
    userId     Int      @map("user_id")
    user       User     @relation(fields: [userId], references: [id])
    total      Decimal  @db.Decimal(10, 2)
    status     String   @default("pending")
    items      OrderItem[]
    createdAt  DateTime @default(now()) @map("created_at")
    updatedAt  DateTime @updatedAt @map("updated_at")

    @@map("orders")
}

model OrderItem {
    id       Int    @id @default(autoincrement())
    orderId  Int    @map("order_id")
    order    Order  @relation(fields: [orderId], references: [id])
    product  String
    quantity Int
    price    Decimal @db.Decimal(10, 2)

    @@map("order_items")
}

Expected output: Schema.prisma defines models, fields, types, relations, indexes, and database mappings. @map and @@map control database column and table names. The schema is the single source of truth.

Generating Migrations

# After changing schema.prisma, create a migration
npx prisma migrate dev --name add_phone_to_users

# Output:
# Environment variables loaded from .env
# Prisma schema loaded from prisma/schema.prisma
# Datasource "db": PostgreSQL database "app_dev"
# 
# Applying migration `20260628120000_add_phone_to_users`
# 
# Your database is now in sync with your schema.
# 
# ✔ Created migration `20260628120000_add_phone_to_users`

# Migration file created:
# prisma/migrations/20260628120000_add_phone_to_users/migration.sql
-- prisma/migrations/20260628120000_add_phone_to_users/migration.sql
-- AlterTable
ALTER TABLE "users" ADD COLUMN "phone" TEXT;

Expected output: Prisma Migrate generates a SQL migration file from schema changes. The migration is applied immediately in development. The migration.sql file is committed to version control.

Applying and Rolling Back

# Apply pending migrations in production
npx prisma migrate deploy

# Reset database (drops all data)
npx prisma migrate reset

# Create migration without applying (for review)
npx prisma migrate dev --create-only --name migration_name

# Rollback in development
npx prisma migrate dev --name previous_migration_name
# Production deployment flow:
# 1. Create migration locally
npx prisma migrate dev --name add_user_role

# 2. Commit migration file to git
git add prisma/migrations/
git commit -m "Add role field to User model"

# 3. Deploy: run migrations on production
npx prisma migrate deploy

# 4. Generate client for new schema
npx prisma generate

Expected output: migrate dev creates and applies migrations in development. migrate deploy applies pending migrations in production. migrate reset drops and recreates the database. migrate deploy is idempotent.

Seeding

// schema.prisma - add seed configuration
// Already in schema.prisma:
generator client {
    provider = "prisma-client-js"
}

datasource db {
    provider = "postgresql"
    url      = env("DATABASE_URL")
}
// prisma/seed.js
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

async function main() {
    // Create users
    const alice = await prisma.user.create({
        data: {
            email: 'alice@example.com',
            name: 'Alice Johnson',
            orders: {
                create: [
                    { total: 59.99, status: 'delivered' },
                    { total: 29.99, status: 'pending' },
                ],
            },
        },
    });

    const bob = await prisma.user.create({
        data: {
            email: 'bob@example.com',
            name: 'Bob Smith',
            orders: {
                create: [
                    { total: 99.99, status: 'paid' },
                ],
            },
        },
    });

    console.log('Seeded:', { alice: alice.id, bob: bob.id });
}

main()
    .catch((e) => {
        console.error(e);
        process.exit(1);
    })
    .finally(async () => {
        await prisma.$disconnect();
    });
// package.json
{
    "prisma": {
        "seed": "node prisma/seed.js"
    }
}
# Run seed
npx prisma db seed

Expected output: Seed script uses Prisma Client to create initial data. The seed is configured in package.json. npx prisma db seed runs the seed. Seeds are idempotent (upsert or check before create).

Client Generation

# Generate Prisma Client for the current schema
npx prisma generate

# The generated client provides type-safe database access
// app.ts - Using Prisma Client
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

async function getUsersWithOrders() {
    const users = await prisma.user.findMany({
        include: {
            orders: {
                include: {
                    items: true,
                },
            },
        },
        where: {
            isActive: true,
        },
    });

    return users;
}

async function createOrder(userId: number, total: number) {
    const order = await prisma.order.create({
        data: {
            userId,
            total,
            status: 'pending',
        },
    });

    return order;
}

// Type-safe: prisma.user.findMany() returns User[]
// Type-safe: prisma.order.create() validates input types

Expected output: prisma generate creates a TypeScript client with full type safety. Autocompletion for models, fields, and relations. Compile-time validation prevents database query errors.

Migration History

# View migration history
npx prisma migrate status

# Output:
# Prisma Migrate is managing the following migrations:
# 
#  20260628120000_init
#  20260628130000_add_phone_to_users
#  20260628140000_create_orders
#  20260628150000_add_order_items
# 
# The following migration have been applied:
# 
#  20260628120000_init
#  20260628130000_add_phone_to_users
# 
# The following migrations have not been applied:
# 
#  20260628140000_create_orders
#  20260628150000_add_order_items

Expected output: migrate status shows the migration history, which migrations are applied, and which are pending. This is useful for CI/CD pipeline verification.

Common Mistakes

1. Editing Migration SQL Files Directly

Prisma generates migration SQL from schema changes. Editing the SQL creates inconsistency between the schema and the migration. If you need custom SQL, use prisma migrate dev --create-only and add SQL before applying.

2. Not Running migrate deploy in Production

Using migrate dev in production is dangerous. migrate dev may reset the database. Always use migrate deploy in production. It only applies pending migrations without modification.

3. Forgetting prisma generate After Migration

After running migrations, you must run prisma generate to update the Prisma Client. The client reflects the new schema. CI/CD pipelines should run migrate deploy then prisma generate.

4. Using Prisma Migrate with an Existing Database

Prisma Migrate expects to manage the full schema. For existing databases, use prisma migrate diff to generate an initial migration from the database. Alternatively, use prisma db pull to introspect.

5. No Seed Idempotency

Seeds that create duplicate records fail on second run. Use upsert or check for existing records before creating. This allows seeds to be run multiple times safely.

Practice Questions

1. How does Prisma Migrate differ from traditional migration tools?

Prisma uses a declarative approach. You define the desired schema in schema.prisma. Prisma generates migrations to match. Traditional tools require writing migration files manually for each change.

2. What is the difference between migrate dev and migrate deploy?

migrate dev is for development: creates and applies migrations, can reset the database. migrate deploy is for production: applies pending migrations without modification, safe for automated deployment.

3. Why must prisma generate be run after migrations?

prisma generate updates the Prisma Client with the new schema. Without it, the client still reflects the old schema. TypeScript types, queries, and validation would be incorrect.

4. How do you handle custom SQL in Prisma migrations?

Use migrate dev --create-only to create the migration file without applying it. Add custom SQL to the generated migration.sql file. Then run migrate dev to apply. The custom SQL is preserved.

Challenge

Set up Prisma for a TypeScript application with: User, Post, and Comment models with relations, migrations for all models, a custom migration with raw SQL for creating a full-text search index, seed script that creates sample data, and deployment script that runs migrate deploy and prisma generate.

FAQ

Can I use Prisma with an existing database?

Yes. Use prisma db pull to introspect an existing database and generate schema.prisma. Then run prisma migrate dev to create the initial migration. Prisma Migrate takes over schema management from there.

Does Prisma support rollback?

Prisma Migrate does not support automatic rollback in production. Use migrate dev --create-only to preview migrations. Create a reverse migration manually if needed.

How does Prisma handle migration conflicts?

Prisma tracks applied migrations in a _prisma_migrations table. Conflicts occur when the migration history diverges. Resolve by creating a migration baseline or using migrate diff.

Can I use Prisma with MySQL or SQLite?

Yes. Prisma supports PostgreSQL, MySQL, MariaDB, SQLite, SQL Server, and MongoDB (for data, not migrations). Change the provider in schema.prisma to switch databases.

What is the best practice for Prisma schema evolution?

Make changes to schema.prisma. Run migrate dev --name description to create and apply. Commit the migration.sql file. In production, run migrate deploy before prisma generate.

Mini Project: Prisma Migration Setup

Set up Prisma for a TypeScript application with: PostgreSQL database, User and Profile models (1:1 relation), Post and Category models (M:N relation), migrations for all models, custom SQL migration for a search index, seed script with sample data, type-safe queries using Prisma Client, and deployment script for CI/CD.

What's Next

Now that you understand Prisma Migrate, learn about Migration Workflow for structuring migration development in team environments.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro