Exercise 2: A Repeatable Sample Seed — Possible Solution ========================================================== // prisma/seeds/sample.ts import { faker } from "@faker-js/faker"; import { prisma } from "../../lib/prisma"; import { ADMIN_EMAIL, REFERENCE_TAGS } from "./reference"; export async function seedSample() { if (process.env.NODE_ENV === "production") { throw new Error("Refusing to seed sample data in production"); } // 1. Clear previous sample data, children first. Keep the admin and reference tags. await prisma.$transaction([ prisma.post.deleteMany({ where: { author: { email: { not: ADMIN_EMAIL } } } }), // comments cascade prisma.user.deleteMany({ where: { email: { not: ADMIN_EMAIL } } }), prisma.tag.deleteMany({ where: { name: { notIn: REFERENCE_TAGS } } }), ]); // 2. Create fresh, repeatable sample data. faker.seed(2026); const tags = await prisma.tag.findMany(); for (let i = 0; i < 10; i++) { const postCount = faker.number.int({ min: 0, max: 5 }); await prisma.user.create({ data: { email: `user${i}@example.com`, // unique by construction name: faker.person.fullName(), posts: { create: Array.from({ length: postCount }, (_, j) => ({ title: faker.lorem.sentence({ min: 3, max: 7 }), slug: `sample-${i}-${j}`, content: faker.lorem.paragraphs(2), published: faker.datatype.boolean({ probability: 0.7 }), viewCount: faker.number.int({ max: 300 }), tags: { connect: faker.helpers .arrayElements(tags, { min: 0, max: 2 }) .map((t) => ({ id: t.id })), }, })), }, }, }); } } (Relation names and onDelete rules are the ones in the Prisma Fundamentals capstone schema — check your own.) WHY THIS WORKS AS AN ANSWER ------------------------------ The production guard runs before anything is deleted. The clean-up deletes posts before the users who wrote them: in the blog schema Post.author is required with the default onDelete rule, so deleting a user who still has posts would fail with P2003. Comments are removed automatically with their posts (onDelete: Cascade), and profiles with their users, so neither needs its own delete. Every delete has a where clause that spares the admin and the reference tags — deleting from a many-to-many table with deleteMany also removes the hidden join rows. Emails and slugs are built from loop counters so they can't collide, and faker.seed(2026) makes names, titles and post counts identical on every run.