Docs / Migrations

Migrations

Plain TypeScript up/down. hopak migrate new · up · down · status.

Schema evolution with history and rollback. Each migration is one .ts file in app/migrations/ with up and down functions.

Subcommands

CommandEffect
hopak migrate initGenerate initial migration from current models (one time)
hopak migrate new <name>Empty skeleton file with up/down
hopak migrate up [--to ID] [--dry-run]Apply pending migrations
hopak migrate down [--steps N] [--to ID]Roll back (default: last 1)
hopak migrate statusApplied / pending / missing

Creating migrations

hopak sync is for the dev bootstrap: it runs CREATE TABLE IF NOT EXISTS on first boot and nothing else. The moment you need to add a column, migrations take over.

hopak migrate init
# → Created app/migrations/20260422T153012345_init.ts (CREATE TABLE for each model)

hopak migrate new add_role_to_user
# → Created app/migrations/20260422T160100_add_role_to_user.ts (empty up/down skeleton)

Fill in the skeleton:

import type { MigrationContext } from '@hopak/core';

export const description = 'Add role column to user';

export async function up(ctx: MigrationContext): Promise<void> {
  await ctx.sql`ALTER TABLE users ADD COLUMN role TEXT DEFAULT 'user'`;
}

export async function down(ctx: MigrationContext): Promise<void> {
  await ctx.sql`ALTER TABLE users DROP COLUMN role`;
}

Applying, inspecting, rolling back

hopak migrate up              # applies pending
hopak migrate up --dry-run    # preview without touching DB
hopak migrate status          # applied / pending / missing
hopak migrate down            # rollback last (or --steps N)

ctx.db inside up/down is the full Hopak client — data migrations (backfill a new column, rewrite rows) live in the same file as their DDL.

Transactional contract

Once app/migrations/ exists, hopak sync refuses to run — schema evolution lives in migrations exclusively. Before that point, sync is still the fastest path from hopak new to a working endpoint.

If you change a model column while still on sync, the next hopak dev prints a drift warning pointing at hopak migrate init — the natural moment to adopt migrations.