Knex Umzug Storage Adapter
knex-umzug is a storage adapter for the umzug database migration library, designed to integrate umzug with knex.js for database interactions. It allows umzug to persist and manage migration states within a relational database using knex as the underlying query builder. The package is currently stable at version 4.1.1, with releases primarily driven by compatibility updates for umzug and knex, alongside bug fixes. A key feature is its support for namespacing and custom migration table names, which enables multiple isolated migration setups to share the same database while maintaining distinct migration states. Furthermore, it tracks comprehensive migration metadata including the current state, all migration paths, the hostname, and the system user who executed each migration, providing a detailed audit trail. This offers enhanced visibility and control over the migration process compared to simpler storage solutions.
Common errors
-
TypeError: Umzug is not a constructor
cause Attempting to `new Umzug()` using CommonJS `require` when `umzug` v3+ is primarily an ESM package, or when using an incorrect import style.fixFor TypeScript/ESM projects, use `import { Umzug } from 'umzug';`. For CommonJS, ensure you're using `const { Umzug } = require('umzug');` (note the destructuring) as `umzug` v3 provides a CJS fallback that exports a named `Umzug`. -
TypeError: KnexUmzug is not a constructor
cause Incorrect import statement for `KnexUmzug`, especially when trying to use named import syntax for a default export.fixUse `import KnexUmzug from 'knex-umzug';` for ESM/TypeScript. For CommonJS, use `const KnexUmzug = require('knex-umzug');`. -
Error: Migration table "umzug_migrations_table" does not exist.
cause The configured migration table does not exist in the database and Umzug/KnexUmzug has not been configured to create it, or there are permission issues.fixEnsure your database user has sufficient permissions to create tables. KnexUmzug will create the table if it doesn't exist upon first `umzug.up()` call, but if there are underlying database connection or permission errors, it might fail silently or with a generic Knex error before reaching this point. -
Error: getaddrinfo ENOTFOUND <database_host>
cause The Knex connection string or configuration points to a database host that cannot be resolved or reached.fixVerify the `connection` details passed to `knex()` constructor are correct, including hostname, port, username, and password. Ensure the database server is running and accessible from where the application is hosted.
Warnings
- breaking Version 4.0.0 of knex-umzug dropped official support for Node.js versions 8 and 10. Users on these older Node.js versions must remain on knex-umzug v3.x or upgrade their Node.js environment.
- breaking With version 4.0.0, a primary key was added to the migrations table by default. While this improves data integrity, existing tables without a primary key might require manual alteration if an issue arises with index creation.
- breaking Version 3.0.0 removed 'legacy storage support' which was originally used internally at livingdocsIO. Although the release notes state it 'won't affect regular users at all', it's a fundamental change in how storage is handled.
- gotcha Compatibility with `umzug` v2 vs. `umzug` v3+ requires different initialization patterns. `knex-umzug` v4.1.0 specifically addressed compatibility with `umzug@3`, but the usage in `storageOptions` differs significantly.
- gotcha When using `knex-umzug` with `umzug` v3+, the migration files themselves should often use ESM syntax (`export const up`, `export const down`). Ensure your Node.js environment is configured to handle ESM, or use a transpiler.
- gotcha The fix in v4.1.1 for 'add support for custom table name when adding id column' implies that prior versions might have had issues with creating the `id` column correctly when a non-default `tableName` was specified for the migration log.
Install
-
npm install knex-umzug -
yarn add knex-umzug -
pnpm add knex-umzug
Imports
- KnexUmzug
import { KnexUmzug } from 'knex-umzug';import KnexUmzug from 'knex-umzug';
- KnexUmzug (CJS)
const KnexUmzug = require('knex-umzug'); - Umzug (from peer dep)
const Umzug = require('umzug');import { Umzug } from 'umzug';
Quickstart
import KnexUmzug from 'knex-umzug';
import { Umzug, migrator } from 'umzug';
import knex from 'knex';
const db = knex({
client: 'sqlite3',
connection: { filename: './migrations.sqlite' },
useNullAsDefault: true // Recommended for sqlite3 with knex
});
// Define your migrations directory
const migrationsPath = `${process.cwd()}/migrations`;
const umzug = new Umzug({
migrations: {
glob: `${migrationsPath}/*.js`,
// Optional: context can be passed to migration files
// context: db
},
storage: new KnexUmzug({
context: 'default',
connection: db,
tableName: 'umzug_migrations_table'
}),
logger: console // Or your preferred logger
});
async function runMigrations() {
console.log('Starting migrations...');
try {
const migrations = await umzug.up();
console.log('Migrations finished successfully!', migrations);
} catch (error) {
console.error('Migration failed:', error);
process.exit(1);
} finally {
await db.destroy(); // Close the database connection
}
}
// Example migration file content (e.g., migrations/001-create-users-table.js)
/*
export const up = async ({ context: sequelize }) => {
// Or knex object if passed via context
// await knex.schema.createTable('users', (table) => {
// table.increments('id').primary();
// table.string('name').notNullable();
// table.timestamps(true, true);
// });
};
export const down = async ({ context: sequelize }) => {
// await knex.schema.dropTable('users');
};
*/
// Create a dummy migration file for demonstration
import fs from 'fs/promises';
import path from 'path';
const dummyMigrationContent = `
export const up = async ({ context: knexInstance }) => {
console.log('Running 001-create-test-table migration UP');
await knexInstance.schema.createTableIfNotExists('test_table', (table) => {
table.increments('id').primary();
table.string('name').notNullable();
table.timestamps(true, true);
});
};
export const down = async ({ context: knexInstance }) => {
console.log('Running 001-create-test-table migration DOWN');
await knexInstance.schema.dropTableIfExists('test_table');
};
`;
async function setupAndRun() {
await fs.mkdir(migrationsPath, { recursive: true });
await fs.writeFile(path.join(migrationsPath, '001-create-test-table.js'), dummyMigrationContent);
await runMigrations();
// Clean up dummy migration file and directory
await fs.unlink(path.join(migrationsPath, '001-create-test-table.js'));
await fs.rmdir(migrationsPath);
}
setupAndRun();