DEV Community

hayata-yamamoto
hayata-yamamoto

Posted on

Next.js and Prisma: Efficiently Creating Seed Data for Your App

When developing applications with Next.js and Prisma, one common challenge is creating seed data for your database. This process can be particularly tricky when working with TypeScript and the latest Next.js features like the App Router. In this blog post, we'll explore an effective approach to generating seed data for your Next.js and Prisma-based projects.

The Challenge

Our scenario involves:

  • A project using TypeScript, Next.js (with App Router), and Prisma
  • PostgreSQL as the database
  • Using prisma migrate for database migrations

The main issue arises when trying to run TypeScript files for seeding data, as the default Next.js configuration doesn't always play nicely with this process.

The Solution

After researching various approaches, we found a solution that doesn't compromise Next.js's recommended settings. The key is to create a separate TypeScript configuration file specifically for seeding data.

Step 1: Set Up the Environment

First, ensure you have ts-node installed:

pnpm add -D ts-node
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a Local TypeScript Configuration

Create a tsconfig.local.json file in your project root. You can start by copying your existing tsconfig.json:

cp tsconfig.json tsconfig.local.json
Enter fullscreen mode Exit fullscreen mode

Then, modify tsconfig.local.json:

{
    "compilerOptions": {
        // ... other options remain the same
        "module": "commonjs"
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Update package.json

Add a prisma.seed script to your package.json:

{
    // ... other configurations
    "prisma": {
        "seed": "ts-node --project tsconfig.local.json prisma/seeds/main.ts"
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Organize Your Seed Files

Structure your seed files like this:

prisma/
    migrations/
    seeds/
        0_users.ts
        1_books.ts
        main.ts
    schema.prisma
Enter fullscreen mode Exit fullscreen mode

Step 5: Write Your Seed Logic

Here's an example of how to structure your seed files:

0_users.ts:

import type { PrismaClient } from "@prisma/client";

const email = "test@example.com";

const seedUsers = async (prisma: PrismaClient) => {
    const start = Date.now();
    console.log("Seeding users...");

    const user = await prisma.user.upsert({
        where: { email },
        update: {},
        create: {
            email,
            name: "Test User",
        },
    });

    const end = Date.now();
    console.log(`Seeding users completed in ${end - start}ms`);

    return user;
};

export default seedUsers;
Enter fullscreen mode Exit fullscreen mode

main.ts:

import { PrismaClient } from "@prisma/client";
import seedUsers from "./0_users";
import seedBooks from "./1_books";

const prisma = new PrismaClient();

async function main() {
    const start = new Date();
    console.log("Seeding database...");

    const user = await seedUsers(prisma);
    const books = await seedBooks(prisma, user.id);

    const end = new Date();
    console.log(`Seeding completed: ${end.getTime() - start.getTime()}ms`);
}

main()
    .then(async () => {
        await prisma.$disconnect();
    })
    .catch(async (e) => {
        console.error(e);
        await prisma.$disconnect();
        process.exit(1);
    });
Enter fullscreen mode Exit fullscreen mode

Step 6: Running Seeds

To run your seeds, you can use a task runner like Make. Here's an example Makefile entry:

prisma-seed-dev:
    dotenv -e .env -- npx prisma db seed
Enter fullscreen mode Exit fullscreen mode

Then, you can run make prisma-seed-dev to execute your seed scripts.

Advanced: Handling Dependencies

For more complex seeding scenarios with dependencies between tables, you can create a simple pipeline using Promises. Here's an example of how you might structure this:

const user = await seedUsers(prisma);
const books = await seedBooks(prisma, user.id);

await Promise.all([
    seedX(prisma, user.id, books[0].id).then(async (x) => {
        await Promise.all([
            seedXX(prisma, user.id, x.id),
            seedXXX(prisma, user.id, x.id)    
        ]);
    }),
    seedY(prisma, books[0].id).then(async (y) => {
        await Promise.all([
            seedYY(prisma, user.id, y.id),
            seedYYY(prisma, user.id, y.id)
        ]);
    })
]);
Enter fullscreen mode Exit fullscreen mode

This approach allows you to handle dependencies between your seed data efficiently.

Conclusion

By using a separate TypeScript configuration for seeding and structuring your seed files carefully, you can create a robust and efficient seeding process for your Next.js and Prisma-based applications. This approach maintains compatibility with Next.js's recommended settings while giving you the flexibility to run TypeScript-based seed scripts.

Remember to adjust the seeding logic based on your specific database schema and requirements. Happy coding!

Top comments (0)