Evolving the Schema
Objectives
By the end of this lesson, you should be able to:
- Add a field to an existing model and migrate the change
- Read a migration file that alters, rather than creates, a table
- Explain what happens to existing rows when a new required field is added
💡 Why this matters: Real schemas change constantly, a new field, a renamed column, a new table. This lesson covers the normal, everyday case: changing a model that’s already been migrated once.
⚠️ A note on verification: as throughout this module, the Prisma CLI can’t run inside this course’s own sandboxed tooling. The commands and output below reflect Prisma’s stable, current, documented behavior. Try it yourself against the project from the last two lessons.
Adding a Field
Add phone to the Student model from the last lesson:
model Student {
id Int @id @default(autoincrement())
name String
grade Int
email String? @unique
phone String?
enrolledAt DateTime @default(now())
}
Migrating the Change
npx prisma migrate dev --name add_phone
Applying migration `20260101130000_add_phone`
The following migration(s) have been created and applied from new schema changes:
migrations/
└─ 20260101130000_add_phone/
└─ migration.sql
Your database is now in sync with your schema.
✔ Generated Prisma Client (v7.x.x) to ./node_modules/@prisma/client in 41ms
-- migrations/20260101130000_add_phone/migration.sql
-- AlterTable
ALTER TABLE "Student" ADD COLUMN "phone" TEXT;
A second, separate migration file, this one an ALTER TABLE, not a CREATE TABLE. Prisma compares the current schema to the last applied migration, and generates only the SQL needed for the difference.
Required Fields on an Existing Table
Adding an optional field (phone String?) is safe, existing rows simply get NULL for it. Adding a required field to a table that already has rows is trickier, PostgreSQL needs a value for every existing row too:
model Student {
id Int @id @default(autoincrement())
name String
grade Int
homeroom String
}
npx prisma migrate dev --name add_homeroom
? We need to reset the "school" database at localhost:5432
You may lose all data in this database.
✔ Do you want to continue? All data will be lost. … yes
If the table already has rows and no default is given, Prisma warns that it can’t safely add a required column without one, and offers to reset the database in development. For an existing table with real data, the safer path is to add the field as optional first, backfill a value for every row, then follow up with a second migration making it required, or provide a @default(...) value so existing rows have something to fall back on:
model Student {
id Int @id @default(autoincrement())
name String
grade Int
homeroom String @default("Unassigned")
}
With a default, the migration succeeds without any data loss, existing rows get "Unassigned", new rows can still specify a real value.
Try It
- Add an optional field to your own
Studentmodel, and migrate it withprisma migrate dev --name <something descriptive>. - Read the generated migration file, and confirm it’s an
ALTER TABLE, not aCREATE TABLE. - Add a required field with a
@default(...)value, migrate it, and confirm existing rows received the default. - Explain, in your own words, why adding a required field with no default is risky on a table that already has data.
Recap
- Adding a field and running
prisma migrate devagain generates anALTER TABLEmigration, only the difference, not the whole schema. - Optional fields are always safe to add to an existing table, required fields need a
@default(...)or a backfill plan. - Every migration is its own file, in order, forming a complete, readable history of how the schema has changed over time.
This is the final lesson of this module before exercises. Next module: modeling relationships between models, one-to-many and many-to-many.