# Migrations

> Generate SQL, diff schemas, and run tracked runtime migrations

Package: Kunii
Canonical: https://kuratchi.dev/docs/kunii/migrations
Markdown: https://kuratchi.dev/docs/kunii/migrations.md

## What the package provides

`kunii/migrations` exposes runtime helpers for schema migration workflows. The implementation tracks migration history in `_kuratchi_migrations`.

## Initial SQL generation

Use the generator helpers when you need SQL from a normalized schema:

```ts
import { buildInitialSql } from '@kuratchi/kunii/migrations';
```

The generated SQL includes:

- `CREATE TABLE IF NOT EXISTS`
- `CREATE INDEX IF NOT EXISTS`

## Diffing schemas

The migration generator can diff two schemas and emit additive SQL plus warnings for unsafe changes.

That is especially important for operations like:

- adding `NOT NULL` columns without defaults
- dropping columns
- changing checks
- rebuilding constraint-heavy definitions

## Runtime migrations

Use `runMigrations()` to apply schema changes at runtime:

```ts
import { runMigrations } from '@kuratchi/kunii/migrations';

await runMigrations({
  execute: (sql, params) => env.DB.prepare(sql).bind(...(params || [])).all(),
  schema: appSchema,
});
```

Behavior:

- first run builds the full schema
- later runs diff the current database and target schema
- identical schema hashes short-circuit
- migration history is recorded in `_kuratchi_migrations`

## Pending checks

Use `hasPendingMigrations()` when you only need to know whether the target schema differs from the last applied migration.

## Cloudflare framework adapters

Most Cloudflare-hosted apps should use a framework adapter instead of calling
`runMigrations()` directly. The adapters wrap the framework's request lifecycle,
run `migrateOnce(env)` for D1 bindings, and then expose ORM clients.

```ts
import { initKuniiORM } from '@kuratchi/kunii';
import { appSchema } from './schemas/app';

const orm = initKuniiORM({ DB: appSchema });

export default {
  async fetch(request, env, ctx) {
    await orm.migrateOnce(env);
    const db = orm.db(env, 'DB');
    return Response.json((await db.todos.many()).data);
  },
};
```

See [Framework Adapters](/docs/kunii/framework-adapters) for SvelteKit, Next, Nuxt,
Astro, Workers, and Koze examples.

## Durable Object additive migrations

`autoMigrate(ctx.storage, schema)` handles Durable Object schema setup differently from tracked D1 runtime migrations:

1. It snapshots existing DO tables.
2. It runs initial `CREATE TABLE IF NOT EXISTS` SQL.
3. It inspects existing columns with `PRAGMA table_info`.
4. It adds missing columns with `ALTER TABLE ... ADD COLUMN`.

That makes DO schema evolution additive by default. There is no migration history table on the DO side — every restart re-applies the idempotent `IF NOT EXISTS` statements, which is cheap and self-correcting.

## Legacy auto-migration middleware

For D1 specifically, `autoMigrate({ DB: schema })` returns a `MiddlewareStep` that calls `runMigrations` on the first request per worker isolate. Drop it into `src/middleware.ts`:

```ts
import { defineMiddleware } from '@kuratchi/koze';
import { autoMigrate } from '@kuratchi/kunii';
import { appSchema } from './server/schemas/app';

export default defineMiddleware({
  migrate: autoMigrate({ DB: appSchema }),
});
```

This is the most common pattern. For total control (running migrations from a queue handler, a scheduled trigger, or a CLI script) call `runMigrations({...})` directly.
