LogoKnex Dart
knex-dartkartikey321/knex-dart 999999

Migrations

Choose one migration source style explicitly: code, SQL directory, or external schema

Migrations#

Knex Dart supports multiple migration source styles. Use one explicit entrypoint so intent is clear in code and docs.

Source Matrix#

SourceEntrypointBest for
Code-first units db.migrate.fromCode([...]) App-defined migration classes/objects in Dart
SQL files on disk db.migrate.fromSqlDir('./migrations') SQL-first teams and DBA-reviewed SQL
SQL files from config db.migrate.fromConfig() Teams standardizing on MigrationConfig.directory
External schema input db.migrate.fromSchema(...) Converting JSON/OpenAPI/custom schema formats into Knex schema AST

All three return a Migrator and share the same lifecycle:

await migrator.latest();
await migrator.rollback();
final status = await migrator.status();

1) Code-First (fromCode)#

final migrator = db.migrate.fromCode([
  const SqlMigration(
    name: '001_create_users',
    upSql: ['create table users (id integer primary key, email varchar(255))'],
    downSql: ['drop table users'],
  ),
]);

await migrator.latest();

2) SQL Directory (fromSqlDir)#

Directory convention:

  • name.up.sql (required)
  • name.down.sql (optional)

Example:

final migrator = db.migrate.fromSqlDir('./migrations');
await migrator.latest();

Notes:

  • Migration id is derived from filename prefix (001_init.up.sql -> 001_init).
  • Files are loaded in lexicographic order.
  • Missing .down.sql means rollback for that migration will fail with a clear error.

2b) SQL Directory from Config (fromConfig)#

fromConfig() is equivalent to fromSqlDir(config.migrations.directory).

final db = Knex(client); // client.config.migrations.directory = './migrations'
await db.migrate.fromConfig().latest();

3) External Schema (fromSchema)#

Convert external schema input to KnexSchemaAst via an adapter, then run as a migration unit.

final migrator = db.migrate.fromSchema(
  name: '001_bootstrap_schema',
  input: jsonSchemaMap,
  ifNotExists: true,
);

await migrator.latest();

Use dropOnDown: true if you want generated rollback to drop table if exists ... for all projected tables.

If no adapter is passed, JsonSchemaAdapter is auto-registered.

Transaction Wrapping Note#

Each migration step can be wrapped in a transaction when disableTransactions: false.

Current default is disableTransactions: true because pooled drivers must pin one physical connection for transactional correctness. SQLite is safe to run with disableTransactions: false because it uses a single connection and supports nested savepoints.

Duplicate Name Rules#

Migration names must be unique across all configured units and sources. Duplicate names throw a KnexMigrationException before execution.

Next Steps#