Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
D1 — Serverless SQL  ›  Lesson

Schema and Migrations — Change the Database in Numbered Steps

Migrations 12 minEvery D1 schema change is written as a new numbered migration file and applied with Wrangler, which records applied files so every environment reaches the same schema.

Schema and Migrations — Change the Database in Numbered Steps

Hoplink's links table needs a new title column. You could open the dashboard and run ALTER TABLE by hand. It works — on that one database. Your teammate's local copy, the preview database and the next new environment know nothing about it, and three weeks later nobody remembers exactly what was run where.

The rule

Every schema change is a numbered file, applied once, in order, everywhere.

A migration is a SQL file describing one change. Wrangler creates them in a migrations/ folder with an increasing number:

npx wrangler d1 migrations create hop-db create_links
# → migrations/0001_create_links.sql
-- migrations/0001_create_links.sql
CREATE TABLE links (
  code       TEXT PRIMARY KEY,
  url        TEXT NOT NULL,
  owner      TEXT NOT NULL,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);

Later changes get their own file, never an edit to an old one:

-- migrations/0002_add_title.sql
ALTER TABLE links ADD COLUMN title TEXT;

You apply them with one command, choosing the target:

npx wrangler d1 migrations list  hop-db --local    # what is still pending?
npx wrangler d1 migrations apply hop-db --local    # your machine
npx wrangler d1 migrations apply hop-db --remote   # the real database

D1 records each applied file in a tracking table (d1_migrations by default). Running apply again only runs files it has not seen, so every environment ends up with the same schema by walking the same numbered steps.

There is still a place for one-off commands. wrangler d1 execute hop-db --remote --command "SELECT COUNT(*) FROM links" is perfect for looking at data. The rule of thumb: reading and investigating can be one-off; anything that changes the shape of the database goes into a migration.

The schema — the collection of tables, columns and constraints — is therefore not something you edit; it is the sum of all migrations. Reading the files in order tells the full history of the database.

The shortcut

Before writing a migration, run the old-code test: "If the new schema is live but the old Worker is still running, does anything break?" Deploys are not instant everywhere, so for a short time both versions meet the same database.

Adding a title to Hoplink's linksStep 1 of 4
  1. Create the file

    wrangler d1 migrations create hop-db add_title → 0002_add_title.sql.

Traps: editing 0001_...sql after it has been applied (environments that already ran it never see the edit), and adding a NOT NULL column without a default, which SQLite rejects outright.

Drills

Answer fast — trust the rule.

quick drill

Migration 0001 is already applied everywhere, and you need a new column. What do you do?

quick drill

How does D1 know which migrations a database still needs?

quick drill

Which command updates the real, deployed database?

quick drill

Why does ALTER TABLE links ADD COLUMN owner_email TEXT NOT NULL; fail?

Remember

  • Every schema change is a numbered file, applied once, in order, everywhere.
  • Shortcut: the old-code test — prefer additive changes old code survives.
  • Trap: editing applied migrations and NOT NULL columns with no default.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainPlan a safe schema change

Hoplink needs an expires_at column on links. Describe the migration file, the commands to apply it locally and remotely, and why the change should be safe for the old Worker still running during the deploy.