Back to Blog
Lesson 20 of the Next.js: Build Full-Stack Apps with the App Router course
Next.jsAugust 7, 20264 min read

Defining the Blog Schema: Prisma Models and Migrations

Learn how to define your database structure with Prisma models, execute safe migrations, and generate a type-safe client for your Next.js blog.

Next.jsPrismaDatabaseMigrationsSchema
Scrabble tiles spelling 'BLOG' on a wooden background, symbolizing creativity and writing.

Previously in this course, we covered the Database Setup with Prisma in Next.js, where we initialized our environment. Now, it’s time to move from configuration to architecture: we will define the actual shape of our data.

In production-grade applications, your database schema is the source of truth for your business logic. By using a Prisma schema, we treat our database structure like code, allowing for version control and type safety across our entire stack.

From Concepts to Prisma Models

A Prisma model represents a table in your database. Each model consists of fields, types, and directives that define constraints (like primary keys or default values).

For our blog, we need at least one core entity: a Post. A standard blog post requires a unique identifier, a title, the content, and timestamps to track when it was created or updated.

Open your prisma/schema.prisma file. You will see a datasource and generator block. Add the following Post model below them:

PRISMA
model Post {
  id        String   @id @default(uuid())
  title     String
  content   String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

Understanding the Model Anatomy

Let’s break down the syntax we just used:

  • @id: Marks the field as the primary key of the table.
  • @default(uuid()): Automatically generates a unique identifier for every new post using the UUID standard.
  • @default(now()): Sets the current timestamp when a record is created.
  • @updatedAt: A special Prisma directive that automatically updates the timestamp whenever the record is modified.

Executing Migrations

Defining the model in the file isn't enough; the database doesn't know these columns exist yet. We must run a migration. Migrations transform your Prisma schema into actual SQL commands executed against your database.

Run the following command in your terminal:

Bash
npx prisma migrate dev --name init

The --name init flag creates a descriptive folder inside prisma/migrations. This folder contains the SQL file that Prisma generated. Check your terminal; you'll see Prisma detected your local database, applied the migration, and triggered the "Prisma Client generation."

Generating the Client

Every time you change your schema and run a migration, you must update the Prisma Client. This client is what provides the auto-completion and type safety you'll use in your Server Components.

While prisma migrate dev runs the generation automatically, in future lessons, you may need to trigger it manually if you make changes without a migration:

Bash
npx prisma generate

Hands-on Exercise

To solidify this, let’s expand our schema. Add a published boolean field to your Post model with a default value of false.

  1. Update your Post model in schema.prisma.
  2. Run npx prisma migrate dev --name add_published_field.
  3. Verify that a new migration file was created in your prisma/migrations folder.

Common Pitfalls

  • Ignoring the node_modules generated client: Sometimes, the IDE might lose track of your types. If you see red squiggles on prisma.post.findMany(), run npx prisma generate to refresh the local types.
  • Manual SQL editing: Never modify your database tables directly using a SQL GUI (like TablePlus or pgAdmin). Always use the schema.prisma file and migrations. If you drift from the schema, your application logic and database state will eventually diverge, leading to runtime errors.
  • Forgetting to commit migrations: Migrations are code. Always commit your prisma/migrations folder to Git so your teammates (or your deployment pipeline) can recreate the database state exactly.

Frequently Asked Questions

Why use UUIDs instead of auto-incrementing integers? UUIDs are more secure for public-facing URLs because they aren't guessable, preventing users from simply incrementing a number (e.g., /posts/1 to /posts/2) to scrape your content.

Can I rename a field after migrating? Yes, but be careful. Renaming a field in schema.prisma and running migrate dev will perform a DROP COLUMN and ADD COLUMN operation, which results in data loss. For production, you usually want to use a "rename" migration strategy or handle the transition in multiple steps.

What is the difference between generate and migrate? migrate updates the physical database structure (SQL). generate reads your schema and updates the TypeScript types in your node_modules so you get IDE support.

Recap

We have successfully modeled our blog data, synchronized it with our database using migrations, and ensured our application is type-safe. You’ve moved from just having a file to having a structured, versioned database schema.

Up next: We will write a seed script to populate our database with initial content so we can start building out our UI components.

Similar Posts