db-migrate and migrate are both Node.js libraries designed to handle database schema changes and data migrations over time. db-migrate is a comprehensive, framework-agnostic tool that supports multiple database drivers (SQL and NoSQL) and offers a robust CLI for generating, running, and rolling back migrations. It is built for complex projects requiring strict version control and multi-environment management. migrate, on the other hand, is a lightweight, minimalist library that focuses on executing JavaScript migration files in order. It provides a simple programmatic API and a basic CLI, making it ideal for developers who prefer to build their own tooling around a simple execution engine without heavy configuration.
Both db-migrate and migrate solve the same core problem: keeping your database schema in sync with your code over time. However, they take very different approaches to get there. db-migrate acts as a full-featured framework with built-in drivers and a powerful CLI, while migrate is a lean library that gives you the execution engine and expects you to bring the rest. Let's look at how they handle real-world engineering scenarios.
db-migrate relies on a configuration file (usually database.json) to define environments, drivers, and connection details. It comes with a built-in CLI that reads this config to manage everything.
# db-migrate: Initialize a project
npx db-migrate init
# Creates a database.json file with sections for dev, test, prod
// db-migrate: database.json
{
"dev": {
"driver": "pg",
"user": "dev_user",
"database": "my_app_dev"
}
}
migrate has no required config file format. You typically create a simple JS file to configure the storage mechanism (where migration state is saved) and the directory containing migration files.
// migrate: config.js
const { Store, Migrator } = require('migrate');
const fs = require('fs');
// Define your own storage logic (e.g., a JSON file or DB table)
const store = new Store({
path: './migrations.json',
fs: fs
});
const migrator = new Migrator({
store: store,
migrationsDirectory: './db/migrations'
});
db-migrate shines with its scaffolding CLI. It generates boilerplate code with up and down functions tailored to your specific database driver.
# db-migrate: Generate a new migration
npx db-migrate create add-users-table
// db-migrate: Generated file (e.g., 20231027123456-add-users-table.js)
exports.up = function(db) {
return db.createTable('users', {
id: { type: 'int', primaryKey: true, autoIncrement: true },
email: { type: 'string', unique: true }
});
};
exports.down = function(db) {
return db.dropTable('users');
};
migrate does not generate files. You create a JS file manually in your migrations folder. The API is generic; you use your own database client (like pg or mysql2) inside the functions.
// migrate: Manual file (e.g., 001-add-users-table.js)
const db = require('./db-client'); // Your custom DB connection
exports.up = async function(next) {
await db.query('CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT UNIQUE)');
next();
};
exports.down = async function(next) {
await db.query('DROP TABLE users');
next();
};
db-migrate provides a rich CLI for all operations. You can run, rollback, reset, and check status directly from the terminal.
# db-migrate: Run all pending migrations
npx db-migrate up
# db-migrate: Rollback the last migration
npx db-migrate down
# db-migrate: Reset database (down then up)
npx db-migrate reset
migrate includes a basic CLI, but its real strength is programmatic usage. You often write a custom script (e.g., npm run migrate) to run the migrator, giving you full control over error handling and logging.
// migrate: Custom run script (run.js)
const { Migrator } = require('migrate');
const config = require('./config');
async function run() {
try {
await config.migrator.up();
console.log('Migrations completed successfully');
} catch (err) {
console.error('Migration failed:', err);
process.exit(1);
}
}
run();
# migrate: Execute via custom script
node run.js
db-migrate tracks state in a dedicated table within your database (e.g., migrations). It knows exactly which migrations ran and in what order, allowing reliable rollbacks using the down export in each file.
// db-migrate: Explicit down function required for rollback
exports.down = function(db) {
// Must reverse the 'up' operation exactly
return db.removeColumn('users', 'age');
};
migrate stores state wherever you tell it to (a JSON file, a DB table, etc.). Rollbacks are possible but rely entirely on you implementing the down logic correctly. The library simply executes the down function of the specific migration you target.
// migrate: Manual rollback execution
await config.migrator.down('001-add-users-table.js');
db-migrate supports a wide range of databases out of the box (PostgreSQL, MySQL, SQLite, MongoDB, Redis, etc.) by using official driver plugins. You just install the driver package (e.g., db-migrate-pg) and update your config.
# db-migrate: Install PostgreSQL driver
npm install db-migrate-pg
migrate is database-agnostic because it doesn't talk to the database itself. You use whatever client library you prefer (pg, mongoose, knex) inside your migration files. This offers maximum flexibility but requires more setup code.
// migrate: Using any client library
const { Pool } = require('pg');
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
exports.up = async function(next) {
const client = await pool.connect();
try {
await client.query('CREATE TABLE ...');
} finally {
client.release();
}
next();
};
Despite their differences, both libraries share core concepts essential for safe database evolution.
Both ensure migrations run in a strict order based on filenames or timestamps, preventing race conditions where tables are created after data is inserted.
// Both enforce order: 001-create-users.js runs before 002-add-email.js
Both rely on the standard up (apply change) and down (revert change) function exports to manage schema evolution and rollbacks.
// Both use this pattern
exports.up = function(...) { ... };
exports.down = function(...) { ... };
While neither forces it, both expect developers to write migrations that can handle being run in a controlled environment, often requiring checks to avoid errors if a migration is accidentally run twice.
// Common practice in both
// CREATE TABLE IF NOT EXISTS users ...
| Feature | db-migrate | migrate |
|---|---|---|
| Philosophy | ποΈ Full Framework | π§© Lightweight Library |
| CLI | β Rich, built-in CLI | βοΈ Basic CLI / Programmatic |
| DB Drivers | π Built-in plugins (SQL/NoSQL) | π Bring your own client |
| Config | π database.json standard | π» Custom JS object |
| Scaffolding | β Auto-generates files | β Manual file creation |
| Best For | Large teams, complex apps | Microservices, custom setups |
db-migrate is like a Swiss Army Knife π¨πβit has every tool you might need built right in. It's perfect for enterprise applications where standardization, multi-db support, and a strong CLI are non-negotiable. If you want to hit the ground running with minimal custom code, this is your choice.
migrate is like a set of high-quality raw materials π§±βit gives you the core engine to execute migrations but leaves the design of the house up to you. It's ideal for developers who already have a preferred database client, want to keep dependencies low, or need to embed migration logic into a custom deployment pipeline.
Final Thought: If you need a solution that "just works" with heavy lifting done for you, pick db-migrate. If you value simplicity and want to own every line of your migration runner, pick migrate.
Choose db-migrate if you are working on a large-scale application that requires support for multiple database types (e.g., PostgreSQL, MySQL, MongoDB) or needs a robust, built-in CLI for managing complex migration lifecycles. It is the better fit for teams that need strict environment separation, automatic rollback capabilities, and a standardized workflow that works out of the box without custom scripting.
Choose migrate if you prefer a minimal dependency that simply runs JavaScript files in sequence and lets you handle the rest (like CLI tools or database connections) yourself. It is ideal for smaller projects, microservices, or teams that want full control over their migration runner and do not need the overhead of a heavy framework with built-in database drivers.
Database migration framework for node.js
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor] or reach out to magic+dbsponsorship@wizardtales.com.
$ npm install -g db-migrate
DB-Migrate is now available to you via:
$ db-migrate
Want to use db-migrate as local module?
$ npm install db-migrate
DB-Migrate is now available to you via:
$ node node_modules/db-migrate/bin/db-migrate
Please follow the link below, for usage instructions examples and the full documentation of db-migrate.
Documentation: https://db-migrate.readthedocs.io/en/latest/
A big thank you to our backers. You're a tremendous and important help, to keep the project healthy! [Become a backer]
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor] or reach out to magic+dbsponsorship@wizardtales.com.
(The MIT License)
Copyright (c) 2015 Tobias Gurtzick
Copyright (c) 2013 Jeff Kunkle
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.