Node DB Migration
node-db-migration is a focused Node.js library designed for managing database schema evolution through simple SQL-based migration scripts. Currently at version 1.4.0, it offers robust support for popular relational databases including SQLite3, MySQL (or its modern fork `mysql2`), and PostgreSQL. Its core philosophy emphasizes using bare SQL files, allowing developers to maintain direct control over their database schemas without being tied to an ORM's migration DSL. The package operates by maintaining a dedicated `migrations` table within the database to track which scripts have been applied. It scans a specified directory for `.sql` files, enforcing a strict `YYYYMMDDHHmm-name.sql` naming convention to ensure chronological execution. Key features include sequential script execution, robust tracking of successful and failed migrations, and the ability to prevent further migrations upon failure until manual intervention. This helps ensure data consistency and provides a clear audit trail of schema changes. While its release cadence isn't rapid, it offers a stable and reliable solution for teams preferring a 'SQL-first' approach to database version control.
Common errors
-
Error: Cannot find module 'pg'
cause The PostgreSQL client library ('pg') is not installed in your project.fixInstall the 'pg' package: `npm install pg` -
Error: ER_PARSE_ERROR: You have an error in your SQL syntax...
cause One of your `.sql` migration files contains invalid SQL syntax.fixCarefully review the SQL code in the indicated migration file for syntax errors specific to your database (e.g., MySQL, PostgreSQL, SQLite). -
Error: SQLITE_ERROR: no such table: migrations
cause The `migrations` table (or your custom `migrationTable` name) does not exist in the database, meaning the `init` command was not run or failed.fixExecute the `init` command to create the migration tracking table: `migrations.run('init');`
Warnings
- gotcha Migration SQL files must strictly adhere to the `YYYYMMDDHHmm-name.sql` naming convention (e.g., `201705231245-add-pets-table.sql`). Incorrect naming will prevent files from being recognized and executed.
- breaking If a migration script fails, `node-db-migration` will record the failure in the `migrations` table and *stop* further migrations until the failed entry is manually resolved. This is a design choice to ensure database consistency.
- gotcha When using MySQL, if your migration scripts contain multiple SQL statements separated by semicolons, you *must* configure your `mysql` or `mysql2` connection with `multipleStatements: true`. Otherwise, only the first statement will execute.
- gotcha The database client (e.g., `pg`, `mysql`, `sqlite3`) must be separately installed as a dependency in your project, as `node-db-migration` only provides the drivers, not the underlying database connectors.
Install
-
npm install node-db-migration -
yarn add node-db-migration -
pnpm add node-db-migration
Imports
- CommandsRunner
const CommandsRunner = require('node-db-migration').CommandsRunner;import { CommandsRunner } from 'node-db-migration'; - MysqlDriver
import MysqlDriver from 'node-db-migration';
import { MysqlDriver } from 'node-db-migration'; - PsqlDriver
let PsqlDriver = require('node-db-migration');import { PsqlDriver } from 'node-db-migration'; - SQLite3Driver
import { SQLite3Driver } from 'node-db-migration';
Quickstart
import { CommandsRunner, PsqlDriver } from 'node-db-migration';
import { Client } from 'pg';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
async function runMigrations() {
const client = new Client({
connectionString: process.env.DATABASE_URL ?? 'postgresql://postgres:@localhost:5432/test_db',
});
try {
await client.connect();
console.log('Connected to PostgreSQL database.');
const migrations = new CommandsRunner({
driver: new PsqlDriver(client),
directoryWithScripts: path.join(__dirname, 'migrations'), // Ensure 'migrations' directory exists with .sql files
migrationTable: 'my_app_migrations' // Optional: custom table name for migration tracking
});
// Example: Create a dummy migration directory and file for demonstration
// In a real app, these would be pre-existing.
const fs = await import('fs/promises');
const migrationsDir = path.join(__dirname, 'migrations');
await fs.mkdir(migrationsDir, { recursive: true });
const migrationFileName = `${new Date().toISOString().replace(/[-:.]/g, '').substring(0, 14)}-create-users-table.sql`;
await fs.writeFile(path.join(migrationsDir, migrationFileName),
'CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, email VARCHAR(255) UNIQUE);');
console.log('Running `migrate` command...');
await migrations.run('migrate');
console.log('Migrations completed successfully.');
// You can run other commands like 'list', 'getFailed', 'resolve'
// console.log('\nListing unapplied migrations:');
// await migrations.run('list');
} catch (error) {
console.error('Migration failed:', error);
process.exit(1);
} finally {
await client.end();
console.log('Database connection closed.');
}
}
runMigrations();