Prisma is an incredible tool for developers working with databases, offering a modern ORM (Object-Relational Mapping) experience. Recently, I dove into Prisma with PostgreSQL for a project and found myself exploring its features hands-on. One task stood out: adding a new model to my Prisma schema and syncing it with my PostgreSQL database. While Prisma’s documentation is thorough, it can feel overwhelming. So, I’m sharing my simplified explanation—along with examples—based on my journey.
In this article, I'll cover how to add a new model, the difference between prisma migrate dev
and prisma migrate deploy
, and why choosing the right command matters depending on your environment.
Let's Add a New Model: The Starting Point
Let’s say I’m building a blog app and already have a User
model in my schema.prisma
file. Now, I want to add a Post
model so users can create blog posts. Here’s what my initial schema.prisma
might look like:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
name String
}
To add the Post
model, I update the schema.prisma
file like this:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
name String
posts Post[] // Relation to Post model
}
model Post {
id Int @id @default(autoincrement())
title String
content String
authorId Int
author User @relation(fields: [authorId], references: [id])
}
After adding the Post
model, I ran:
npx prisma generate
This updates the Prisma Client with the new model, but my PostgreSQL database doesn’t reflect this change yet. To sync the database, I need to use Prisma’s migration commands.
That’s where prisma migrate dev
and prisma migrate deploy
come in—but which one should I use, and why?
Understanding prisma migrate dev
Since I’m working locally, I opted for prisma migrate dev
. Here’s what it does, step by step:
Detects Schema Drifts: It compares my
schema.prisma
file with the actual database structure. If there’s a mismatch (e.g., a missing table or column), it suggests changes. Be cautious here—applying these suggestions unwisely can lead to data loss. (I’ll cover how to handle this safely in a future article.)Generates a Migration File: It creates a SQL migration file in the prisma/migrations folder based on the changes. For my
Post
model, the file might look like this:
CREATE TABLE "Post" (
"id" SERIAL PRIMARY KEY,
"title" TEXT NOT NULL,
"content" TEXT NOT NULL,
"authorId" INTEGER NOT NULL,
FOREIGN KEY ("authorId") REFERENCES "User"("id")
);
Applies the Migration: It runs the SQL against my local PostgreSQL database, creating the
Post
table.Saves Migration History: It stores a record of the migration (with a
SHA256
hash) in a special_prisma_migrations
table. This tracks which migrations have been applied.Updates the Prisma Client: It regenerates the
Prisma Client
to include the newPost
model, so I can query it in my code.
To run this, I used:
npx prisma migrate dev --name add_post_model
The --name
flag gives the migration a descriptive name (e.g., add_post_model
). After running it, I can now use the Post
model in my app:
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
async function createPost() {
const post = await prisma.post.create({
data: {
title: 'My First Post',
content: 'Hello, Prisma!',
authorId: 1,
},
});
console.log(post);
}
createPost();
This command is perfect for local development because it handles everything—drift detection, migration creation, and client updates—in one go.
Why Use prisma migrate deploy
Elsewhere?
Now, imagine I’m ready to push my app to production or a testing environment. Here, prisma migrate dev
isn’t the right choice. Instead, I use:
npx prisma migrate deploy
Here’s why: prisma migrate deploy
is more limited—and that’s intentional. It only does two things:
Applies Existing Migration Files: It runs the SQL files in the
prisma/migrations
folder against the database, in the order they were created.Updates the Migration History: It ensures the
_prisma_migrations
table reflects the applied migrations.
It skips drift detection, migration file generation, and Prisma Client regeneration (Steps 1, 2, 4, and 5 from migrate dev). This makes it safer and more predictable in production:
No Drift Detection: Production databases shouldn’t be auto-corrected based on schema drift. That could lead to unintended changes or data loss.
No New Migrations: Migration files should be created and tested locally, then committed to version control—not generated on the fly in production.
No Client Regeneration: The Prisma Client is typically built during the app’s deployment process, not during migration.
For example, after running prisma migrate dev
locally and committing the migration files, I deploy my app to production. On the production server, I run:
npx prisma migrate deploy
This applies the add_post_model
migration to the production PostgreSQL database, ensuring it matches my local setup without extra steps.
Key Takeaways
Use prisma migrate dev
in development to create and apply migrations, detect drifts, and update the Prisma Client. It’s a full-featured tool for experimenting locally.
Use prisma migrate deploy
in production or testing to apply pre-existing migrations safely, without altering the database beyond what’s planned.
Always test migrations locally first to avoid surprises in production.
Prisma’s migration system is powerful, but the distinction between these commands confused me at first. The official docs are detailed, but I hope this simpler breakdown—complete with examples—helps you get started faster.
Stay tuned for my next article on preventing data loss during migrations!
Some Important Links:
Top comments (0)