MOTIVATION
Welcome, everyone! Today, we’re diving into an essential part of building
real-world applications: seeding data and handling migrations using Prisma.js.
Why does this matter? Imagine you’re creating a blogging app—like the one we’ll
build together in this lecture. You want users to see posts right away and
admins to manage them, but how do you set up that initial data? And what happens
when your app evolves, and you need to change your database structure? That’s
where seeding and migrations come in—they’re the backbone of keeping your
database organised and functional.
Let’s start with a relatable problem: You’ve built a blog app, but when you
launch it, the homepage is empty—no posts! Users leave, frustrated. Seeding
fixes that by populating your database with starter data. Now, imagine you
decide to add a "tags" feature to filter posts, but your database doesn’t know
about tags yet. Migrations let you update your database safely without breaking
everything. These tools are critical for any developer, and mastering them will
save you headaches down the road.
Here’s a common misconception: “I’ll just manually add data or tweak my database
when I need to.” That works for a tiny project, but in the real world, you need
a systematic approach with teams and live users. Let’s bust another myth:
“Migrations are scary and might delete my data.” Done right, they’re safe and
powerful. Ready to see how? Let’s jump in with our blog app example.
🛳️ UNDERSTANDING PRISMA MIGRATIONS
First, let’s tackle migrations. Prisma is an ORM (Object-Relational Mapping)
tool that makes working with databases easier. Migrations are how Prisma keeps
your database schema in sync with your code. Think of it like updating the
blueprint of a house as you add new rooms. Prisma offers two main commands for
this: migrate and db push. Let’s explore both using our blog app.
HOW MIGRATIONS WORK
We will start with a simple schema in schema.prisma:
model Post {
id Int @id @default(autoincrement())
title String
content String
createdAt DateTime @default(now())
}
To apply this to your database, you have two options:
OPTION 1: <STRONG>PRISMA MIGRATE DEV</STRONG>
Run:
pnpx prisma migrate dev --name init
This creates a migration file (e.g., 20250310120000_init) in a migrations
folder. It’s a SQL script that tells your database (like PostgreSQL) to create
the Post table. Prisma tracks every change in these files, building a history of
your database’s evolution. Now, let’s add tags:
model Post {
id Int @id @default(autoincrement())
title String
content String
createdAt DateTime @default(now())
tags String[]
}
Run:
npx prisma migrate dev --name add_tags
Prisma compares the old schema to the new one, generates a new migration file to
add the tags column, and applies it. Existing posts get an empty tags array by
default. This is great for production apps because it’s controlled, repeatable,
and reversible.
OPTION 2: <STRONG>PRISMA DB PUSH</STRONG>
Instead, you could run:
npx prisma db push
This directly updates your database to match the schema—no migration files, no
history. After adding tags, run it again, and the database instantly reflects
the change. It’s faster but less structured.
KEY DIFFERENCES: <STRONG>MIGRATE</STRONG> VS. <STRONG>DB PUSH</STRONG>
* Tracking: migrate dev creates a versioned history (migration files); db push
doesn’t—it just syncs the database.
* Use Case: Use migrate dev for team projects or production where you need
control and rollback options. Use db push for quick prototyping or solo
development.
* Safety: migrate lets you review and tweak changes before applying; db push
applies them immediately, which can risk data loss if you’re not careful
(e.g., dropping a column deletes its data).
* Example in Action: For our blog app, migrate dev ensures we can share schema
changes with a team and deploy safely. With db push, we’d skip the paperwork
but lose the ability to undo mistakes.
STEP-BY-STEP EXAMPLE WITH BOTH
1. Initial Setup: Define the Post model and run npx prisma migrate dev --name
init. The table is created with a migration file.
2. User Feedback: “Add tags to filter posts!” Update the schema with tags.
3. Migrate Path: Run npx prisma migrate dev --name add_tags. A new migration
file adds the column, and we’re ready for production.
4. Push Path: Alternatively, run npx prisma db push. The database updates
instantly—no files, no fuss, but no history either.
5. Result: Both get us a tags column, but migrate gives us structure, while
push gives us speed.
ADVANTAGES AND DISADVANTAGES
* Migrate:
* Pros: Version control, safe for teams, rollback possible.
* Cons: Slower setup, requires managing migration files.
* Db Push:
* Pros: Fast, simple, great for testing.
* Cons: No history, riskier for production, harder to collaborate.
🌱 SEEDING DATA WITH PRISMA
Now, let’s populate our blog with data using seeding. Seeding means preloading
your database with initial data—like sample posts—so it’s not empty when users
arrive.
HOW IT WORKS
Create a seed.ts file in the prisma folder:
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
await prisma.post.createMany({
data: [
{ title: "My First Post", content: "Hello, world!", tags: ["intro"] },
{ title: "Tech Trends", content: "AI is the future.", tags: ["tech"] },
],
});
}
main()
.then(() => prisma.$disconnect())
.catch((e) => {
console.error(e);
prisma.$disconnect();
process.exit(1);
});
Update package.json:
"scripts": {
"prisma:seed": "ts-node prisma/seed.ts"
}
After migrating (or pushing), run:
npm run prisma:seed
Your database now has two posts, ready for users to filter by tags.
EXPANDING THE EXAMPLE
For a more complex seed, add dates and more tags:
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
await prisma.post.deleteMany(); // Clear existing data (optional)
await prisma.post.createMany({
data: [
{
title: "My First Post",
content: "Hello, world!",
createdAt: new Date("2025-01-01"),
tags: ["intro", "welcome"],
},
{
title: "Tech Trends",
content: "AI is the future.",
createdAt: new Date("2025-02-15"),
tags: ["tech", "innovation"],
},
{
title: "Travel Diary",
content: "Exploring the mountains.",
createdAt: new Date("2025-03-01"),
tags: ["travel", "nature"],
},
],
});
console.log("Database seeded!");
}
main()
.then(() => prisma.$disconnect())
.catch((e) => {
console.error(e);
prisma.$disconnect();
process.exit(1);
});
Run it again, and your app has a rich starting point.
ADVANTAGES AND DISADVANTAGES
* Advantages: Quick setup, consistent test data, easy to tweak.
* Disadvantages: Can overwrite data if not careful, and large seeds slow things
down.
SECTION 3: PUTTING IT TOGETHER IN OUR BLOG APP
Our blog app ties migrations and seeding together:
* Client Side: Users filter posts by tags (e.g., /posts?tag=travel).
* Admin Side: Admins add/edit posts.
WORKFLOW
1. Define the schema in schema.prisma.
2. Use migrate dev for production or db push for prototyping to apply changes.
3. Seed data with npm run prisma:seed.
4. Query posts with Prisma’s client:
const posts = await prisma.post.findMany({
where: { tags: { has: "tech" } },
});
REAL-WORLD TWIST
“Won’t db push break my app?” Only if you’re reckless—test first! “Seeding feels
fake!” It’s a standard way to mimic real usage.
CONCLUSION
Today, we mastered Prisma migrations and seeding for our blog app.
* Key Takeaways: migrate dev for structure, db push for speed, seeding for
instant data.
* Best Practices: Use migrate in teams, test db push carefully, keep seeds
realistic.
* Pitfalls: Don’t mix migrate and push carelessly, avoid untested schema
changes.
You’re ready to build a dynamic blog app. Next, we’ll query this data to power
your features. Great job—keep exploring Prisma!
Maggie is a generative AI that can help you understand the course content better. You can ask her questions about the lecture, and she will try to answer them. You can also see the questions asked by other students and her responses.
Join the discussion to ask questions, share your thoughts, and discuss with other learners