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.

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:
PRISMAmodel 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:
Bashnpx 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:
Bashnpx 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.
- Update your
Postmodel inschema.prisma. - Run
npx prisma migrate dev --name add_published_field. - Verify that a new migration file was created in your
prisma/migrationsfolder.
Common Pitfalls
- Ignoring the
node_modulesgenerated client: Sometimes, the IDE might lose track of your types. If you see red squiggles onprisma.post.findMany(), runnpx prisma generateto 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.prismafile 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/migrationsfolder 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.
Work with me

Next.js Full-Stack Web App Development
A fast, SEO-ready full-stack web app built with Next.js 16 — from idea to deployed product, by an engineer who ships to production.

Next.js E-commerce Store Development
Turn your Facebook page or small shop into a real online store — fast, mobile-first, and built to sell. Own your storefront, not just a social page.

