Migrations#
Knex Dart supports multiple migration source styles. Use one explicit entrypoint so intent is clear in code and docs.
Source Matrix#
| Source | Entrypoint | Best 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.sqlmeans 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.