Usage of db-migrate

To use db-migrate, you call it via the command line. When entering only the command without paramaters you will see something like this:

Usage: db-migrate [up|down|reset|create|db] [[dbname/]migrationName|all] [options]

Down migrations are run in reverse run order, so migrationName is ignored for down migrations.
Use the --count option to control how many down migrations are run (default is 1).

Options:
  --env, -e                   The environment to run the migrations under.    [default: "dev"]
  --migrations-dir, -m        The directory containing your migration files.  [default: "./migrations"]
  --count, -c                 Max number of migrations to run.
  --dry-run                   Prints the SQL but doesn't run it.              [boolean]
  --verbose, -v               Verbose mode.                                   [default: false]
  --config                    Location of the database.json file.             [default: "./database.json"]
  --force-exit                Call system.exit() after migration run          [default: false]
  --sql-file                  Create sql files for up and down.               [default: false]
  --coffee-file               Create a coffeescript migration file            [default: false]
  --migration-table           Set the name of the migration table.
  --table, --migration-table                                                  [default: "migrations"]

Creating Migrations

To create a migration, execute db-migrate create with a title. node-db-migrate will create a node module within ./migrations/ which contains the following two exports:

exports.up = function (db, callback) {
  callback();
};

exports.down = function (db, callback) {
  callback();
};

Note: In newer versions of db-migrate, we have included a promise-based interface. In these newer versions, the create command will generate a file containing the following:

exports.up = function(db) {
    return null;
};

exports.down = function(db) {
    return null;
};

All you have to do is populate these, invoking callback() or returning the result of your db operation when complete, and you are ready to migrate!

For example:

$ db-migrate create add-pets
$ db-migrate create add-owners

The first call creates ./migrations/20111219120000-add-pets.js, which we can populate:

/* Callback-based version */
exports.up = function (db, callback) {
  db.createTable('pets', {
    id: { type: 'int', primaryKey: true },
    name: 'string'
  }, callback);
};

exports.down = function (db, callback) {
  db.dropTable('pets', callback);
};
/* Promise-based version */
exports.up = function (db) {
  return db.createTable('pets', {
    id: { type: 'int', primaryKey: true },
    name: 'string'
  });
};

exports.down = function (db) {
  return db.dropTable('pets');
};

The second creates ./migrations/20111219120005-add-owners.js, which we can populate:

/* Callback-based version */
exports.up = function (db, callback) {
  db.createTable('owners', {
    id: { type: 'int', primaryKey: true },
    name: 'string'
  }, callback);
};

exports.down = function (db, callback) {
  db.dropTable('owners', callback);
};
/* Promise-based version */
exports.up = function (db) {
  return db.createTable('owners', {
    id: { type: 'int', primaryKey: true },
    name: 'string'
  });
};

exports.down = function (db) {
  return db.dropTable('owners');
};

Executing multiple statements against the database within a single migration requires a bit more care. You can either nest the migrations like:

/* Callback-based version */
exports.up = function (db, callback) {
  db.createTable('pets', {
    id: { type: 'int', primaryKey: true },
    name: 'string'
  }, createOwners);

  function createOwners(err) {
    if (err) { callback(err); return; }
    db.createTable('owners', {
      id: { type: 'int', primaryKey: true },
      name: 'string'
    }, callback);
  }
};

exports.down = function (db, callback) {
  db.dropTable('pets', function(err) {
    if (err) { callback(err); return; }
    db.dropTable('owners', callback);
  });
};
/* Promise-based version */
exports.up = function (db) {
  return db.createTable('pets', {
    id: { type: 'int', primaryKey: true },
    name: 'string'
  })
  .then(
    function(result) {
      db.createTable('owners', {
        id: { type: 'int', primaryKey: true },
        name: 'string'
      });
    },
    function(err) {
      return;
    }
  );
};

exports.down = function (db) {
  return db.dropTable('pets')
    .then(
      function(result) {
        db.dropTable('owners');
      },
      function(err) {
        return;
      }
    );
};

or use the async library to simplify things a bit, such as:

var async = require('async');

exports.up = function (db, callback) {
  async.series([
    db.createTable.bind(db, 'pets', {
      id: { type: 'int', primaryKey: true },
      name: 'string'
    }),
    db.createTable.bind(db, 'owners', {
      id: { type: 'int', primaryKey: true },
      name: 'string'
    })
  ], callback);
};

exports.down = function (db, callback) {
  async.series([
    db.dropTable.bind(db, 'pets'),
    db.dropTable.bind(db, 'owners')
  ], callback);
};

Using files for sqls

If you prefer to use sql files for your up and down statements, you can use the --sql-file option to automatically generate these files and the javascript code that load them.

For example:

$ db-migrate create add-people --sql-file

This call creates 3 files:

./migrations/20111219120000-add-people.js
./migrations/sqls/20111219120000-add-people-up.sql
./migrations/sqls/20111219120000-add-people-down.sql

The sql files will have the following content:

/* Replace with your SQL commands */

And the javascript file with the following code that load these sql files:

var dbm;
var type;
var fs = require('fs');
var path = require('path');

/**
  * We receive the dbmigrate dependency from dbmigrate initially.
  * This enables us to not have to rely on NODE_PATH.
  */
exports.setup = function(options) {
  dbm = options.dbmigrate;
  type = dbm.datatype;
};

exports.up = function(db, callback) {
  var filePath = path.join(__dirname + '/sqls/20111219120000-add-people-up.sql');
  fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){
    if (err) return console.log(err);
    db.runSql(data, function(err) {
      if (err) return console.log(err);
      callback();
    });
  });
};

exports.down = function(db, callback) {
  var filePath = path.join(__dirname + '/sqls/20111219120000-add-people-down.sql');
  fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){
    if (err) return console.log(err);
    db.runSql(data, function(err) {
      if (err) return console.log(err);
      callback();
    });
  });
};

Making it as default

To not need to always specify the sql-file option in your db-migrate create commands, you can set a property in your database.json as follows:

{
    "dev": {
      "host": "localhost",
    ...
  },
    "sql-file" : true
}

Running Migrations

When first running the migrations, all will be executed in sequence. A table named migrations will also be created in your database to track which migrations have been applied.

  $ db-migrate up
  [INFO] Processed migration 20111219120000-add-pets
  [INFO] Processed migration 20111219120005-add-owners
  [INFO] Done

Subsequent attempts to run these migrations will result in the following output

  $ db-migrate up
  [INFO] No migrations to run
  [INFO] Done

If we were to create another migration using db-migrate create, and then execute migrations again, we would execute only those not previously executed:

  $ db-migrate up
  [INFO] Processed migration 20111220120210-add-kennels
  [INFO] Done

You can also run migrations incrementally by specifying a date substring. The example below will run all migrations created on or before December 19, 2011:

  $ db-migrate up 20111219
  [INFO] Processed migration 20111219120000-add-pets
  [INFO] Processed migration 20111219120005-add-owners
  [INFO] Done

You can also run a specific number of migrations with the -c option:

  $ db-migrate up -c 1
  [INFO] Processed migration 20111219120000-add-pets
  [INFO] Done

All of the down migrations work identically to the up migrations by substituting the word down for up.