Migrations using ORM2's model DSL leveraging Visionmedia's node-migrate.
Heads up ! v2 introduce some major changes, make sure you check the changelog
npm install migrate-orm2
The example below uses MySQL. Locomote uses migrate-orm2 with Postgres. Testing was also done with SQLite3, though some driver issues were encountered.
Build a connection & construct the migrate-orm2 Task object:
var orm = require('orm');
var MigrateTask = require('migrate-orm2');
orm.connect(connectionString, function (err, connection) {
if (err) throw err;
var task = new MigrateTask(connection.driver);
});
The Task constructor function can support options allowing for a custom migrations directory and/or coffeescript support (see 'Usage - opts' below).
A Task object offers three operations - generate, up and down.
> task.generate('create-users', function(err, result){});
> create : /Users/nicholasf/code/locomote/node-migrate-orm2/migrations/001-create-users.js
The 'migrations' folder is the default but can be overridden in the opts argument (see 'Usage - opts' below).
A skeleton migration file now exists and can be populated with the ORM2 DSL.
A simple example, taken from the tests:
exports.up = function (next) {
this.createTable('test_table', {
id : { type : "serial", key: true }, // auto increment
name : { type : "text", required: true }
}, next);
};
exports.down = function (next){
this.dropTable('test_table', next);
};
Another example for adding or dropping a column:
exports.up = function(next){
this.addColumn('agency', preferredProvider: {type: "text", defaultValue: '1G', required: true}, next);
}
exports.down = function(next){
this.dropColumn('agency', 'preferredProvider', next);
}
An example of adding an index:
exports.up = function (next) {
this.addIndex('agency_email_idx', {
table: 'agency',
columns: ['email'],
unique: true
}, next);
};
exports.down = function (next) {
this.dropIndex('agency_email_idx', 'agency', next);
};
There are no built-in operations for inserting, updating, or deleting row data contained in tables. The execQuery
operation, which can be used to execute any custom queries, can however be used to perform such actions:
exports.up = function (next) {
this.execQuery('INSERT INTO agency (email) VALUES (?)', ['info@example.com'], next);
};
exports.down = function (next) {
this.execQuery('DELETE FROM agency WHERE email = ?', ['info@example.com'], next);
};
The full list of operations available through this
:
- createTable
- dropTable
- addColumn
- dropColumn
- addIndex
- dropIndex
- addPrimaryKey
- dropPrimaryKey
- addForeignKey
- dropForeignKey
- execQuery
These operations are depicted in the examples folder.
We would like to add modifyColumn functionality in the future.
> task.up(function(e,r){});
> up : migrations/001-create-users.js
migration : complete
Alternatively, when there are many migrations, a filename can be specified:
> task.generate('create-servers', function(err, result){});
> create : /Users/nicholasf/code/locomote/node-migrate-orm2/migrations/002-create-servers.js
> task.up('001-create-users.js', function(e,r){})
> up : migrations/001-create-users.js
migration : complete
This means 'run up to this migration then execute its up function, then stop.'
> task.down(function(e,r){});
> down : migrations/001-create-users.js
migration : complete
This means 'rollback the last migration'.
If there are many migrations to rollback a limit can be specified.
> task.down('001-create-users.js', function(e,r){});
> down : migrations/001-create-users.js
migration : complete
This means 'rollback all migrations including 001-create-users.js'
Migrate-orm2 maintains an internal orm_migrations table which allows it to run from previous state.
The table contains a list of migrations applied to the database. By comparing these and the migration files, we can decide on which migrations to run.
mysql> select * from orm_migrations;
+---------------------+
| migration |
+---------------------+
| 001-create-users.js |
+---------------------+
1 rows in set (0.00 sec)
The Task object can be modified to work from a different directory or to generate and cooperate with coffee-script migrations.
var task = new Task(connection, {dir: 'data/migrations', coffee: true});
See https://github.com/nicholasf/node-orm-migrate for a command line tool.
♪ node-orm-migrate git:(master) ✗ migrate --help
Usage: migrate [options]
Options:
-h, --help output usage information
-V, --version output the version number
-g, --generate Generate a migration
-u, --up Run up migrations
-d, --down Run down migrations
We handcraft grunt and our tasks looks this.
Firstly, we have a helper file which knows how to build the connection and opts and invoke the Task object:
var MigrationTask = require('migrate-orm2');
var orm = require('orm');
exports.runMigration = function (operation, grunt, done) {
orm.settings.set("connection.debug", true);
orm.connect('mysql://root@localhost/ninja', function (err, connection) {
if (err) throw(err);
var migrationTask = new MigrationTask(
connection.driver,
{ dir: 'data/migrations'}
);
migrationTask[operation](grunt.option('file'), done);
});
};
Registering the Grunt tasks looks like this:
grunt.registerTask('migrate:generate', '', function () {
var done = this.async();
require('./tasks/db').runMigration('generate', grunt, done);
});
grunt.registerTask('migrate:up', '', function () {
var done = this.async();
require('./tasks/db').runMigration('up', grunt, done);
});
grunt.registerTask('migrate:down', '', function () {
var done = this.async();
require('./tasks/db').runMigration('down', grunt, done);
});
To generate a migration file or to indicate a direction:
grunt migrate:generate --file=create-users
grunt migrate:generate --file=create-servers
grunt migrate:up --file=001-create-users.js
Please note - running all of the tests together can produce database connection pooling problems. We are currently considering these.
Tests work in isolation and when the database is tuned for a greater amount of database connections.
Create test/config.js
(see test/config.example.js
for instructions)
npm test
This will run the tests against all configurations inside config.js
.
To run against a single config:
ORM_PROTOCOL=mysql node test/run
# OR
ORM_PROTOCOL=mysql mocha test/integration
Contributions are welcome. If you want to discuss or request a feature, please open an issue.
We will ask for test coverage of Pull Requests for most issues. Please see the current testing strategy in test/integration.
- nicholasf
- dxg
- vaskas
- benkitzelman
- sidorares
- wolfeidau
- damsonn
This work is a melding of two underlying libraries:
- node-migrate from @visionmedia (TJ)
- node-sql-ddl-sync from @dresende