| 1 | --- |
| 2 | title: Migrations |
| 3 | description: Apply SQL migrations and seed data to Neon projects and branches as part of every deploy — ordered, hashed, and tracked in Alchemy's __alchemy_migrations table. |
| 4 | --- |
| 5 | |
| 6 | Both `Neon.Project` and `Neon.Branch` accept a `migrations` prop — a |
| 7 | folder of `.sql` files (or a [`Drizzle.Schema`](/sql/drizzle/migrations) |
| 8 | resource) applied in order as part of every deploy. On a project, |
| 9 | migrations run against the default branch's primary database; on a |
| 10 | branch, they run against the branch itself: |
| 11 | |
| 12 | ```typescript |
| 13 | const project = yield* Neon.Project("my-project", { |
| 14 | migrations: "./migrations", |
| 15 | }); |
| 16 | ``` |
| 17 | |
| 18 | Because branches are copy-on-write forks, a branch created from a |
| 19 | migrated parent already contains the parent's schema — and its |
| 20 | tracking table. Point the branch at the same `migrations` directory and only |
| 21 | migrations added after the fork are applied: |
| 22 | |
| 23 | ```typescript |
| 24 | const featureBranch = yield* Neon.Branch("feature", { |
| 25 | project, |
| 26 | migrations: "./migrations", |
| 27 | }); |
| 28 | ``` |
| 29 | |
| 30 | ## Ordering and hashing |
| 31 | |
| 32 | Files are discovered recursively under the migrations directory and sorted by |
| 33 | their numeric prefix (`0001_init.sql`, `0002_users.sql`, …), falling |
| 34 | back to name order for files without one. Each file's contents are |
| 35 | SHA-256 hashed and the hashes are persisted in the resource's state |
| 36 | (`migrationsHashes`), so adding a migration file — or editing an |
| 37 | existing one — is what marks the resource for an update on the next |
| 38 | deploy. When nothing changed, the migration step is skipped |
| 39 | entirely. |
| 40 | |
| 41 | ## The tracking table |
| 42 | |
| 43 | Applied migrations are recorded in Alchemy's `__alchemy_migrations` |
| 44 | table (created automatically), one row per file: |
| 45 | |
| 46 | ```sql |
| 47 | CREATE TABLE IF NOT EXISTS "__alchemy_migrations" ( |
| 48 | id SERIAL PRIMARY KEY, |
| 49 | hash text NOT NULL, |
| 50 | created_at bigint, |
| 51 | name text, |
| 52 | applied_at timestamp with time zone DEFAULT now() |
| 53 | ); |
| 54 | ``` |
| 55 | |
| 56 | A database previously migrated with drizzle-kit or Prisma is |
| 57 | [adopted automatically](/sql/effect-sql/migrations#adopting-an-existing-database): |
| 58 | its old tracking table's history is copied in once and the old table |
| 59 | is frozen. Projects deployed by older Alchemy versions (which used a |
| 60 | `neon_migrations` table) are upgraded in place. |
| 61 | |
| 62 | On each deploy, files whose names already appear in the table are |
| 63 | skipped. Use `migrations: { dir, table }` to put the bookkeeping in |
| 64 | a differently-named table: |
| 65 | |
| 66 | ```typescript |
| 67 | const project = yield* Neon.Project("my-project", { |
| 68 | migrations: { dir: "./migrations", table: "my_migrations" }, |
| 69 | }); |
| 70 | ``` |
| 71 | |
| 72 | ## Transactional apply |
| 73 | |
| 74 | Each pending migration runs inside a transaction together with its |
| 75 | bookkeeping `INSERT` into the tracking table, so partial application |
| 76 | is impossible: a failing statement rolls the whole file back and |
| 77 | fails the deploy, while migrations that already committed stay |
| 78 | applied. Fix the file and re-deploy — the run resumes from the |
| 79 | failed migration. |
| 80 | |
| 81 | ## Seed data with importFiles |
| 82 | |
| 83 | `importFiles` lists additional `.sql` files to apply after |
| 84 | migrations. Paths are resolved relative to the working directory: |
| 85 | |
| 86 | ```typescript |
| 87 | const project = yield* Neon.Project("my-project", { |
| 88 | migrations: "./migrations", |
| 89 | importFiles: ["./seed/users.sql"], |
| 90 | }); |
| 91 | ``` |
| 92 | |
| 93 | Unlike migrations, import files are not recorded in the tracking |
| 94 | table. Each file is content-hashed and re-applied whenever its |
| 95 | contents change, so write them to be safe to run more than once |
| 96 | (`INSERT ... ON CONFLICT DO NOTHING`, `CREATE OR REPLACE`, and the |
| 97 | like). Files whose hashes match the previous deploy are skipped. |
| 98 | |
| 99 | ## Where next |
| 100 | |
| 101 | Guides: |
| 102 | |
| 103 | - [Add Drizzle ORM](/cloudflare/data/drizzle) — typed schemas and |
| 104 | generated migrations. |
| 105 | |
| 106 | Related: |
| 107 | |
| 108 | - [Neon overview](/neon) — projects, branches, and composing with |
| 109 | your cloud. |
| 110 | - [Setup](/neon/setup) — credentials and provider registration. |
| 111 | |
| 112 | Reference: |
| 113 | |
| 114 | - [Project API reference](/providers/neon/project) |
| 115 | - [Branch API reference](/providers/neon/branch) |