Skip to main content
The @talosjs/migrations component is a database migration runner for Bun’s native SQL client. Each migration is a class implementing IMigration with up and down methods, a timestamp-based version, and an optional dependency list. Migrations are registered with a decorator, sorted by version, and run inside a transaction. Applied versions are tracked in a database table so each one runs exactly once.

How migrations behave

Every migration carries a timestamp version (YYYYMMDDHHMMSSMMM), and the runner sorts by version before applying them in order. The up() method applies a change and down() reverses it, which keeps schema changes symmetric and reviewable. Each migration runs inside sql.begin(), so a failure rolls back the whole migration and stops the run. Applied versions are recorded in a tracking table (default migrations), and anything already applied is skipped on the next run. A migration can also declare other migrations as dependencies, and those run first regardless of timestamp. As with the rest of the framework, you register a migration class with a decorator and the runner resolves it from the container.

How it works

You write a migration class that implements IMigration and register it with @decorator.migration(). The up() runner loads every registered migration, sorts it by version, and applies each pending one inside a transaction, recording the version when it succeeds. The up() runner drives the lifecycle: The tracking table stores one row per applied version. Before running a migration, the runner checks the table for its id; if it’s already there, the migration is skipped. Passing --drop to the runner first drops and recreates the public schema. That is destructive, so reserve it for development.

Environment variables

Usage

A migration is a class implementing IMigration, registered with the decorator. up() applies the change and down() reverses it:
Run all pending migrations with up(). It reads DATABASE_URL by default, or takes an explicit connection string and tracking table name:
Declare dependencies to guarantee ordering beyond the version timestamp. A migration’s dependencies always run first:

Writing migrations that hold up

Make down() the exact reverse of up(): drop precisely what up adds, since an asymmetric or irreversible down() is a bug. Once a migration has run on a shared database, never edit it. Add a new corrective migration instead of rewriting one that already ran. Add indexes for foreign keys, WHERE and ORDER BY columns, and unique constraints in up(), then drop them in down() before the table or column they cover. Keep column types, nullability, and lengths in sync with the entity definition, so a non-nullable column is also NOT NULL in the migration. Reserve --drop for development; it wipes the whole schema and must never touch a shared or production database. When one change has to precede another regardless of timestamps, declare it in getDependencies().

CLI command

Scaffold a migration with the generator. talos migration:create writes a timestamped migration class under modules/<module>/src/migrations/, a matching test under modules/<module>/tests/migrations/, refreshes the migrations.ts barrel export, and creates the module’s bin/migration/up.ts and bin/migration/down.ts runners if they are missing.
The migration file name is derived from a timestamp version automatically, so there is no --name option. Capture the schema change when you implement up() and down(). The generated class is a ready-to-fill stub:
Apply pending migrations across every module with talos migration:up:
Roll back applied migrations with talos migration:down. Each rollback runs the migration’s down() in a transaction and removes its row from the migrations table, so a later migration:up re-applies it:
See migration:create, migration:up, and migration:down for the full command references.

Use with Claude and Codex

The generator ships a matching migration:create skill. It runs the scaffold, then guides your AI agent through completing the migration: implementing up() with the schema change, down() with the reverse, and the indexes your queries need. Initialize the skills once for your agent.
Then ask Claude in natural language. It maps the request to the generator, runs it, and fills in the implementation:
Prompt
For example, the prompt above maps to migration:create, then implements up() to add the avatar column and down() to drop it.