Seeding & Test Data
Prisma Intermediate/Advanced
Chapter 4 · Seeding & Test Data
A fresh database is empty. Before you can click around the blog, try a new feature, or run the tests in the next chapter, you need data in it — ideally the same data every time, created by one command. That's what a seed script is for.
Two Kinds of Seed Data
| Kind | Examples | Where it runs |
|---|---|---|
| Reference data | Required categories, an admin account, default settings | Every environment, including production |
| Sample data | Fifty fake users, two hundred posts | Development and testing only |
Keep them separate. Fake users in production are a real, and embarrassing, mistake.
Configuring the Seed Command
In Prisma 7 the seed command lives in the Prisma config file (prisma.config.ts, or
prisma7.config.ts from 7.10 — see Prisma Fundamentals, Chapter 2), under
migrations:
migrate dev and migrate reset ran the seed script for you. Prisma 7
removed that. After resetting a database, run npx prisma db seed yourself — or add an npm script
that does both, such as "db:fresh": "prisma migrate reset --force && prisma db seed".
A First Seed Script
It works once. Run it a second time and it fails with P2002: ada@example.com already
exists. A seed script that can only run on an empty database is fragile.
Idempotent Seeds
An idempotent script gives the same end result however many times you run it. There are two common ways to get there.
Option 1: upsert by a unique field
Best for reference data: it's safe to run against a database that already holds real data, because it only touches the rows it names.
Option 2: clear, then create
Best for sample data in development. Delete children before parents, following each
relation's onDelete rule. In the blog schema from Prisma Fundamentals, Post.author is
required and uses the default rule, so deleting a user who still has posts fails with P2003;
Profile and Comment are handled automatically (a profile is deleted with its user,
comments are deleted with their post, and a comment's author is set to null when the user goes).
Never run this against production — guard it:
Realistic Fake Data With Faker
faker.seed(42) makes Faker produce the same sequence every run. A bug you found in "the seeded data"
will still be there tomorrow, and a teammate running the same script gets the same database. Change the number
when you want a different set.
@unique column that throws P2002. The
slugs above avoid it by building from the loop counters; for emails, add the index
(`user${i}@example.com`) or use Faker's unique helpers where your version provides them.
Speed: createMany
One create per row means one database round trip per row. For flat data without nested relations,
createMany inserts everything in one statement:
createMany can't create nested relations, and skipDuplicates isn't supported on every
database (SQLite, MongoDB and SQL Server don't support it). Check before relying on it; for those databases, use
upsert or filter out existing rows first.
Organising a Bigger Seed
prisma/
├── seed.ts <- decides what to run
└── seeds/
├── reference.ts <- tags, admin user (upserts; safe anywhere)
└── sample.ts <- fake users and posts (dev/test only)
Hands-On Exercises
Write seeds/reference.ts: an admin user and five fixed tags, fully idempotent. Run it three times and show the row counts don't change.
Write seeds/sample.ts: clear the sample data, then create 10 users with 0–5 posts each using a seeded Faker. Make sure it can't run in production and never deletes the admin user or the reference tags.
Add npm scripts so that one command resets the database, applies every migration and seeds it. Explain why this matters more in Prisma 7 than it did in Prisma 6.
📄 View solutionChapter 4 Quick Reference
- Configure
migrations.seed(e.g."tsx prisma/seed.ts") in the Prisma config file - Run with
npx prisma db seed— Prisma 7 no longer seeds automatically aftermigrate dev/reset - Reference data:
upsertby a unique field; safe in every environment - Sample data: delete children before parents, then create; guard against production
faker.seed(n)gives repeatable fake data; build unique fields from counterscreateManyfor flat bulk inserts; no nested relations;skipDuplicatesisn't available on every database