# Generating a migration (/docs/orm/next/migrations/generating-a-migration)

Location: ORM > Next > Migrations > Generating a migration

This tutorial takes a contract change from your editor to a planned migration on disk. Planning is **fully offline**: `migration plan` reads your emitted contract and your existing migrations; it never connects to a database, so you can plan on a plane, in CI, or in a sandbox with no credentials.

Start from a minimal project; `npx create-prisma@next` scaffolds one.

Your first migration [#your-first-migration]

Say your contract has a single model:

```prisma title="contract.prisma"
model User {
  id    Int     @id
  email String
  name  String?

  @@map("user")
}
```

First, emit the contract. This compiles the schema into the `contract.json` artifact that every other command reads:

```bash
npx prisma-next contract emit
```

Now plan. `--name` sets the human-readable part of the directory name; without it the directory is called `migration`:

```bash
npx prisma-next migration plan --name init
```

```text
✔ Planned 2 operation(s)

│
├─ Create schema "public"
└─ Create table "user"

from:   null
to:     sha256:705b1a62f26f0913caa4bfe3f8b7cb491a1b94bd47fc43471d8711bc480bcbb5
App space → migrations/app/20260707T1005_init

Next: review migrations/app/20260707T1005_init if needed, then run prisma-next migrate.

DDL preview

CREATE SCHEMA IF NOT EXISTS "public";
CREATE TABLE "public"."user" (
  "email" text NOT NULL,
  "id" int4 NOT NULL,
  "name" text,
  PRIMARY KEY ("id")
);
```

Four things to notice:

* **The DDL preview is right there.** You see the exact SQL before anything exists but files.
* **`from: null`** means this migration starts from an empty database; it's the root of your [migration graph](/orm/next/migrations/the-migration-graph).
* **`to:` is your contract's hash.** The migration promises to deliver a database matching exactly the contract you just emitted.
* **`App space`** is your application's migration lane. Database extensions bring their own lanes; see [extension spaces](/orm/next/migrations/applying-a-migration#extension-spaces).

The planned migration directory contains the TypeScript source, the compiled operations, and the contract snapshots:

```text
migrations/app/20260707T1005_init/
├── migration.ts
├── ops.json
├── migration.json
└── end-contract.json  (+ end-contract.d.ts)
```

Open `migration.ts`. It reads like a description of the change, because it is one:

```ts title="migrations/app/20260707T1005_init/migration.ts"
import type { Contract as End } from './end-contract';
import endContract from './end-contract.json' with { type: 'json' };
import { Migration, MigrationCLI, col, primaryKey } from '@prisma-next/postgres/migration';

export default class M extends Migration<never, End> {
  override readonly endContractJson = endContract;

  override get operations() {
    return [
      this.createSchema({ schema: 'public' }),
      this.createTable({
        schema: 'public',
        table: 'user',
        columns: [
          col('email', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
          col('id', 'int4', { notNull: true, codecRef: { codecId: 'pg/int4@1' } }),
          col('name', 'text', { codecRef: { codecId: 'pg/text@1' } }),
        ],
        constraints: [primaryKey(['id'])],
      }),
    ];
  }
}

MigrationCLI.run(import.meta.url, M);
```

Two notes on reading it: the `col(...)` calls mirror your contract fields, and you never write `codecRef` by hand. The planner fills it in to pin how values convert between TypeScript and Postgres; treat it as noise when reviewing.

For a simple change you don't need to touch this file. When you do (to add a data backfill, reorder operations, or drop in raw SQL), that's [Editing a migration](/orm/next/migrations/editing-a-migration).

The second migration: planning a delta [#the-second-migration-planning-a-delta]

Apply the first migration (covered properly in [Applying a migration](/orm/next/migrations/applying-a-migration)), then make another change: add a `phone` field:

```prisma
model User {
  id    Int     @id
  email String
  name  String?
  phone String?

  @@map("user")
}
```

Emit and plan again. This time, tell the planner where to start from, so it plans the *delta* rather than the whole schema:

```bash
npx prisma-next contract emit
npx prisma-next migration plan --name add_user_phone --from 20260707T1005_init
```

```text
✔ Planned 1 operation(s)

│
└─ Add column "phone" to "user"

from:   sha256:705b1a62f26f0913caa4bfe3f8b7cb491a1b94bd47fc43471d8711bc480bcbb5
to:     sha256:925198f3cc272c5fd19c24ac02f251661775ddac21cdac4e634bbc0dda8b2d72
App space → migrations/app/20260707T1006_add_user_phone

DDL preview

ALTER TABLE "public"."user" ADD COLUMN "phone" text;
```

`--from` accepts a migration directory name (as here), a contract hash or unambiguous prefix, a ref name, or `<dir>^` meaning "the state *before* that migration".

The db ref: skipping --from [#the-db-ref-skipping---from]

Passing `--from` every time gets old. The planner's default is smarter: if a [ref](/orm/next/migrations/the-migration-graph#name-important-states-with-refs) named **`db`** exists, planning starts from whatever it points at. Keep it advanced as part of applying:

```bash
npx prisma-next migrate --advance-ref db
npx prisma-next migration plan --name next_change   # starts from the db ref automatically
```

> [!WARNING]
> Without
> 
> `--from`
> 
>  or a
> 
> `db`
> 
>  ref, plans start from empty
> 
> If there's no `db` ref and you omit `--from`, `migration plan` plans from an **empty database**: a full `CREATE`-everything migration, not a delta. If you expected a one-column change and got a wall of `CREATE TABLE`, this is why. Pass `--from` or set up the `db` ref.

When the planner needs your input [#when-the-planner-needs-your-input]

Some changes can't be planned from the schema diff alone. Add a **required** field to a table that already has rows, and the planner can write the `ADD COLUMN` and the `SET NOT NULL`, but only you know what the existing rows should contain. Rather than guessing, it scaffolds the decision as a **placeholder**:

```text
⚠ Planned migration with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit

Open migration.ts and replace each `placeholder(...)` call with your actual query.
Then run: node migrations/app/20260707T1008_add_display_name/migration.ts
```

The generated migration sandwiches a `dataTransform` between the schema steps, with `placeholder(...)` where your backfill query goes. Filling it in is the subject of [Editing a migration](/orm/next/migrations/editing-a-migration).

Reviewing what you planned [#reviewing-what-you-planned]

Three offline commands close the loop before anything runs:

```bash
# One migration in detail: operations, metadata, DDL preview
npx prisma-next migration show 20260707T1006_add_user_phone

# The whole graph, with your new edge in place
npx prisma-next migration graph

# Integrity check: hashes match, files complete, graph well-formed; exits non-zero on failure
npx prisma-next migration check
```

`migration check` is designed for CI: exit code `0` means clean, `2` means it couldn't resolve what you asked for, `4` means an integrity failure, for example someone hand-edited `ops.json` without re-running `migration.ts`.

> [!NOTE]
> What's early
> 
> Planning covers tables, columns, indexes, constraints, and the backfill scaffold shown above. Rename inference is not built yet: renaming a field plans as **drop column + add column**, flagged destructive with a data-loss warning. For a true rename, edit the migration and replace the pair with a `rawSql` `ALTER TABLE ... RENAME COLUMN`. There is also no interactive mode; the planner writes its best answer and leaves refinement to you in `migration.ts`.

Prompt your coding agent [#prompt-your-coding-agent]

Projects scaffolded with `create-prisma@next` install [Prisma Next skills](/ai/tools/skills#available-skills-for-prisma-next) for your coding agent. Ask your agent to:

* "Add a required `displayName` field to User, emit the contract, and plan the migration."
* "Plan a migration named `add-orders-table` and show me its DDL preview before I commit it."
* "Run `migration check` and explain any integrity failures."

See also [#see-also]

* [Editing a migration](/orm/next/migrations/editing-a-migration): fill placeholders, add data steps, write raw SQL
* [Applying a migration](/orm/next/migrations/applying-a-migration): run what you planned
* [Studio with Prisma Next](/studio/prisma-next): once applied, see the same operations as a visual diff in Prisma Studio
* [TypeScript Migrations in Prisma Next](https://www.prisma.io/blog/typescript-migrations-in-prisma-next): the design story behind `migration.ts`

## Related pages

- [`Applying a migration`](https://www.prisma.io/docs/orm/next/migrations/applying-a-migration): prisma-next migrate walks the graph from where your database is to where you want it, with a preview, a checkpoint at every step, and safe retries.
- [`Editing a migration`](https://www.prisma.io/docs/orm/next/migrations/editing-a-migration): A migration is TypeScript you own. Fill in backfills, reorder steps, or drop to raw SQL, then recompile it with one command.
- [`How migrations work`](https://www.prisma.io/docs/orm/next/migrations/how-migrations-work): Change your contract, plan a migration, review it, apply it. Every step is checked before and after it runs.
- [`Rollbacks and recovery`](https://www.prisma.io/docs/orm/next/migrations/rollbacks-and-recovery): Rolling back is planning one more migration to a state you've already been in. Recovery is fixing the cause and re-running, safely.
- [`The migration graph`](https://www.prisma.io/docs/orm/next/migrations/the-migration-graph): You changed your schema, a teammate changed theirs, both merged. The migration graph is what lets every database catch up without renaming files or rebuilding history.