Conditional migrations in NodeJS using Sequelize

| Mar 17, 2014 min read

I’m running SequelizeJS as my ORM for Enki. Recently I wanted to add a location field to tasks so users could “geolocate” the work items they were adding in world space. Click on a task and Sextant will zoom you to that point on the map where the work needs to be done. All you need to do is bring a shovel and a hard-hat.

I’d already done one migration earlier on to add an active field to tasks so I knew the basic commands to fire off:

node_modules/sequelize/bin/./sequelize -c 0.0.2

… Edit some code to migrate …

node_modules/sequelize/bin/./sequelize -m
[BIZZ-BLGXXL-CRACKLE-SPIT] Column Exists [FOO-BRAKL-BARK-BARK-HISS]

OOPS

But this time when I went to run the migration, I hit an error. It seems that because it fires all migrations in sequence, if you add a column in an earlier migration you really need to wrap it in a test to see if the column exists first.

Here’s my code:

up: function(migration, DataTypes, done) {
    // add altering commands here, calling 'done' when finished
    migration.migrator.sequelize.query(
        "show fields from Tasks where field = 'active';"
    )
    .success(
        function (records) {
                if (records.length > 0) {
                    done();
                    return;
                }
                migration.addColumn(
                    "Tasks",
                    "active",
                    DataTypes.BOOLEAN
                );
                done();
        }
    );
}

This is only the up-migration path but the down would be pretty much identical. Thinking about migrations on a longer timeline it becomes very clear that this portion of your application needs to do the most amount of defensive driving. You have to assume that before the migration ran; you cannot guarantee what the state of the database is going to be.

Gotcha

One other thing that’s important to point out, by accessing the sequelize object buried inside of the migrator tool; you have access to all the asynchronous goodness of Sequelize. You need to remember to invoke the “done()” callback in both cases, otherwise the versions check during migration will stop on this case. Hopefully this little chunk of code helps someone out of a bind.