SkillAgentSearch skills...

Umzug

Framework agnostic migration tool for Node.js

Install / Use

/learn @sequelize/Umzug
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Umzug

Build Status npm npm (downloads)

Umzug is a framework-agnostic migration tool for Node. It provides a clean API for running and rolling back tasks.

Highlights

  • Written in TypeScript
    • Built-in typings
    • Auto-completion right in your IDE
    • Documentation right in your IDE
  • Programmatic API for migrations
  • Built-in CLI
  • Database agnostic
  • Supports logging of migration process
  • Supports multiple storages for migration data
  • Usage examples

Documentation

Note: these are the docs for the latest version of umzug, which has several breaking changes from v2.x. See the upgrading section for a migration guide. For the previous stable version, please refer to the v2.x branch.

Minimal Example

The following example uses a Sqlite database through sequelize and persists the migration data in the database itself through the sequelize storage. There are several more involved examples covering a few different scenarios in the examples folder. Note that although this uses Sequelize, Umzug isn't coupled to Sequelize, it's just one of the (most commonly-used) supported storages.

// index.js
const {Sequelize} = require('sequelize')
const {Umzug, SequelizeStorage} = require('umzug')

const sequelize = new Sequelize({dialect: 'sqlite', storage: './db.sqlite'})

const umzug = new Umzug({
  migrations: {glob: 'migrations/*.js'},
  context: sequelize.getQueryInterface(),
  storage: new SequelizeStorage({sequelize}),
  logger: console,
})

// Checks migrations and run them if they are not already applied. To keep
// track of the executed migrations, a table (and sequelize model) called SequelizeMeta
// will be automatically created (if it doesn't exist already) and parsed.
await umzug.up()
// migrations/00_initial.js

const {Sequelize} = require('sequelize')

async function up({context: queryInterface}) {
  await queryInterface.createTable('users', {
    id: {
      type: Sequelize.INTEGER,
      allowNull: false,
      primaryKey: true,
    },
    name: {
      type: Sequelize.STRING,
      allowNull: false,
    },
    createdAt: {
      type: Sequelize.DATE,
      allowNull: false,
    },
    updatedAt: {
      type: Sequelize.DATE,
      allowNull: false,
    },
  })
}

async function down({context: queryInterface}) {
  await queryInterface.dropTable('users')
}

module.exports = {up, down}

Note that we renamed the context argument to queryInterface for clarity. The context is whatever we specified when creating the Umzug instance in index.js.

<details> <summary>You can also write your migrations in typescript by using `ts-node` in the entrypoint:</summary>
// index.ts
require('ts-node/register')

import { Sequelize } from 'sequelize';
import { Umzug, SequelizeStorage } from 'umzug';

const sequelize = new Sequelize({ dialect: 'sqlite', storage: './db.sqlite' });

const umzug = new Umzug({
  migrations: { glob: 'migrations/*.ts' },
  context: sequelize.getQueryInterface(),
  storage: new SequelizeStorage({ sequelize }),
  logger: console,
});

// export the type helper exposed by umzug, which will have the `context` argument typed correctly
export type Migration = typeof umzug._types.migration;

(async () => {
  await umzug.up();
})();
// migrations/00_initial.ts
import type { Migration } from '..';

// types will now be available for `queryInterface`
export const up: Migration = ({ context: queryInterface }) => queryInterface.createTable(...)
export const down: Migration = ({ context: queryInterface }) => queryInterface.dropTable(...)
</details>

See these tests for more examples of Umzug usage, including:

  • passing ignore and cwd parameters to the glob instructions
  • customising migrations ordering
  • finding migrations from multiple different directories
  • using non-js file extensions via a custom resolver, e.g. .sql

Usage

Installation

Umzug is available on npm by specifying the correct tag:

npm install umzug

Umzug instance

It is possible to configure an Umzug instance by passing an object to the constructor.

const {Umzug} = require('umzug')

const umzug = new Umzug({
  /* ... options ... */
})

Detailed documentation for these options are in the UmzugOptions TypeScript interface, which can be found in src/types.ts.

Getting all pending migrations

You can get a list of pending (i.e. not yet executed) migrations with the pending() method:

const migrations = await umzug.pending()
// returns an array of all pending migrations.

Getting all executed migrations

You can get a list of already executed migrations with the executed() method:

const migrations = await umzug.executed()
// returns an array of all already executed migrations

Executing pending migrations

The up method can be used to execute all pending migrations.

const migrations = await umzug.up()
// returns an array of all executed migrations

It is also possible to pass the name of a migration in order to just run the migrations from the current state to the passed migration name (inclusive).

await umzug.up({to: '20141101203500-task'})

To limit the number of migrations that are run, step can be used:

// This will run the next two migrations
await umzug.up({step: 2})

Running specific migrations while ignoring the right order, can be done like this:

await umzug.up({migrations: ['20141101203500-task', '20141101203501-task-2']})

Reverting executed migration

The down method can be used to revert the last executed migration.

const migration = await umzug.down()
// reverts the last migration and returns it.

To revert more than one migration, you can use step:

// This will revert the last two migrations
await umzug.down({step: 2})

It is possible to pass the name of a migration until which (inclusive) the migrations should be reverted. This allows the reverting of multiple migrations at once.

const migrations = await umzug.down({to: '20141031080000-task'})
// returns an array of all reverted migrations.

To revert all migrations, you can pass 0 as the to parameter:

await umzug.down({to: 0})

Reverting specific migrations while ignoring the right order, can be done like this:

await umzug.down({migrations: ['20141101203500-task', '20141101203501-task-2']})

Migrations

There are two ways to specify migrations: via files or directly via an array of migrations.

Migration files

A migration file ideally exposes an up and a down async functions. They will perform the task of upgrading or downgrading the database.

module.exports = {
  async up() {
    /* ... */
  },
  async down() {
    /* ... */
  },
}

Migration files can be located anywhere - they will typically be loaded according to a glob pattern provided to the Umzug constructor.

Direct migrations list

You can also specify directly a list of migrations to the Umzug constructor:

const {Umzug} = require('umzug')

const umzug = new Umzug({
  migrations: [
    {
      // the name of the migration is mandatory
      name: '00-first-migration',
      async up({context}) {
        /* ... */
      },
      async down({context}) {
        /* ... */
      },
    },
    {
      name: '01-foo-bar-migration',
      async up({context}) {
        /* ... */
      },
      async down({context}) {
        /* ... */
      },
    },
  ],
  context: sequelize.getQueryInterface(),
  logger: console,
})

Modifying the parameters passed to your migration methods

Sometimes it's necessary to modify the parameters umzug will pass to your migration methods when the library calls the up and down methods for each migration. This is the case when using migrations currently generated using sequelize-cli. In this case you can use the resolve fuction during migration configuration to determine which parameters will be passed to the relevant method

import {Sequelize} from 'sequelize'
import {Umzug, SequelizeStorage} from 'umzug'

const sequelize = new Sequelize(/* ... */)

const umzug = new Umzug({
  migrations: {
    glob: 'migrations/*.js',
    resolve: ({name, path, context}) => {
      const migration = require(path)
      return {
        // adjust the parameters Umzug will
        // pass to migration methods when called
        name,
        up: async () => migration.up(context, Sequelize),
        down: async () => migration.down(context, Sequelize),
      }
    },
  },
  context: sequelize.getQueryInterface(),
  storage: new SequelizeStorage({sequelize}),
  logger: console,
})

Additional migration configuration options

To load migrations in another format, you can use the resolve function:

const fs = require('fs')
const {Sequelize} = require('sequelize')
const {Umzug} = require('umzug')

const umzug = new Umzug({
  migrations: {
    glob: 'migrations/*.up.sql',
    resolve: ({name, path, context: sequelize}) => ({
      name,
      up: async () => {
        const sql = fs.readFileSync(path).toString()
        return sequelize.query(sql)
      },
      down: async () => {
        // Get the corresponding `.down.sql` file to undo this migration
        const sql = fs
          .readFileSync(path.replace('.up.sql', '.down.sql'))
          .toString()
        return sequelize.query(sql)
      },
    }),
  },
  context: new Sequelize(/* ... */),
  logger: console,
})

You can support mixed migration file types, and use umzug

View on GitHub
GitHub Stars2.2k
CategoryDevelopment
Updated5d ago
Forks164

Languages

TypeScript

Security Score

100/100

Audited on Apr 1, 2026

No findings