How to programmatically run sequelize migrations

15.8k Views Asked by At

The documentation for sequelize seems out of date as they no longer support running migrations from sequelize itself, but instead relies on sequelize-cli. Is there an example of how to use sequeliz-cli programmatically to run the latest migrations? All the documentation seems to be focused on using the client in a shell.

db.js seems to have the function db:migrate that perhaps I can include.

https://github.com/sequelize/cli/blob/master/lib/tasks/db.js

3

There are 3 best solutions below

3
On BEST ANSWER

Update: @phil-court's solution is better.

Original answer here for posterity...


I dug into the code for the sequelize db:migrate command, and there's enough going on there that, IMHO, the simplest/best approach is to just run the command in a child process. Here's the code I used for this (as an await'ed Promise):

const {exec} = require('child_process');

await new Promise((resolve, reject) => {
  const migrate = exec(
    'sequelize db:migrate',
    {env: process.env},
    err => (err ? reject(err): resolve())
  );

  // Forward stdout+stderr to this process
  migrate.stdout.pipe(process.stdout);
  migrate.stderr.pipe(process.stderr);
});
2
On

I had this exact same problem and implemented the accepted answer. However I ran into concurrency issues while running this as a separate process, especially during tests.

I think this question is rather old, but it still appears very high on search results. Today it's a much better idea to run it using umzug. It's the library that sequelize uses to manage migrations on it's end, and is suggested by the docs.

const fs = require('fs');
const Umzug = require('umzug');
const path = require('path');
const Sequelize = require('sequelize');
const { sequelize } = require('../models/index.js');

const umzug = new Umzug({
  migrations: {
    // indicates the folder containing the migration .js files
    path: path.join(process.cwd(), './migrations'),
    // inject sequelize's QueryInterface in the migrations
    params: [
      sequelize.getQueryInterface(),
      Sequelize,
    ],
  },
  // indicates that the migration data should be store in the database
  // itself through sequelize. The default configuration creates a table
  // named `SequelizeMeta`.
  storage: 'sequelize',
  storageOptions: {
    sequelize,
  },
});

async function migrate() {
  return umzug.up();
}

async function revert() {
  return umzug.down({ to: 0 });

And with that you can do everything you need to do with migrations without resorting to spawning a different process, which opens you to all sorts of race conditions and problems down the line. Read more about how to use umzug with the docs on github

0
On

This is what I did. It is not yet extensively tested, and probably can be optimized further:

const Sequelize = require('sequelize');
const db = new Sequelize('main', 'test', 'test', {
dialect: 'sqlite',
// SQLite only
storage: 'db.db'
});

async function checkForMigrations() {
let migrations = fs.readdirSync(__dirname + '/../migrations');
let completedMigrations = await db.query("SELECT * FROM `SequelizeMeta`", {type: Sequelize.QueryTypes.SELECT});
for (let name in completedMigrations) {
    if (completedMigrations.hasOwnProperty(name)) {
        let index = migrations.indexOf(completedMigrations[name].name);
        if (index !== -1) {
            migrations.splice(index, 1);
        }
    }
}

for(let i = 0, c = migrations.length; i < c; i++){
   let migration = require(__dirname + '/../migrations/' + migrations[i]);
   migration.up(db.queryInterface, Sequelize);
   await db.query("INSERT INTO `SequelizeMeta` VALUES(:name)", {type: Sequelize.QueryTypes.INSERT, replacements: {name: migrations[i]}})
}
}