Umzug

repository·main·Indexed 25 days ago

https://github.com/sequelize/umzug

A framework-agnostic and database-agnostic migration tool for Node.js designed to run and roll back tasks like database migrations. It supports various storage backends, provides a programmatic API for executing migrations via up() and down() methods, and includes a CLI for managing the migration lifecycle. Umzug is written in TypeScript and supports ECMAScript modules (ESM), raw SQL clients, and custom templates for migration creation.

Tokens
9.2K
Snippets
24
Records
55
Agent score
79%

What's inside umzug

  1. What is Umzug?

    main
    Umzug is a framework-agnostic migration tool for Node.js. It provides a clean, programmatic API for running and rolling back tasks (such as database migrations). It is database-agnostic, meaning it can be used with any database system, and supports multiple storage mechanisms for tracking migration state. It is written in TypeScript and provides built-in typings for IDE auto-completion and documentation.
  2. Use Umzug with bundlers via codegen

    main

    When using a bundler (like esbuild, webpack, parcel, bun, etc.) to package migrations for different environments, you cannot rely on Umzug's default filesystem globbing because the bundler may not include files discovered via runtime filesystem scans.

    To solve this, use a code generation (codegen) pattern:

    1. Use a script to glob migration files before bundling.
    2. Import those files into a 'barrel' file (a single object containing all migrations).
    3. Pass that barrel object directly to the Umzug constructor using the migrations.resolve option (or by providing the object directly if the constructor supports it) so the bundler can statically analyze and include the migrations.

    This technique works with any bundling library that supports standard JavaScript imports.

  3. Configure Umzug Storages

    main

    Storages determine where migration metadata is persisted. Supported storages include:

    • JSONStorage: Persists migrations in a JSON file (default: umzug.json).
    • memoryStorage: In-memory storage, ideal for tests.
    • SequelizeStorage: Uses a SequelizeMeta table in a SQL database.
    • MongoDBStorage: Uses a migrations collection in MongoDB.
    • Custom Storage: Any object implementing the UmzugStorage interface.
    import { Umzug, memoryStorage } from 'umzug'
    
    const umzug = new Umzug({
      migrations: ..., 
      storage: memoryStorage(),
      logger: console,
    })
  4. Define migrations via files or direct list

    main

    Umzug supports two ways to define migrations:

    1. Migration Files

    Files typically export up and down async functions. They are loaded using a glob pattern in the migrations configuration.

    2. Direct Migrations List

    You can pass an array of migration objects directly to the migrations key. Each object must have a name and up/down functions.

    const umzug = new Umzug({
      migrations: [
        {
          name: '00-first-migration',
          async up({context}) { /* ... */ },
          async down({context}) { /* ... */ },
        }
      ],
      context: sequelize.getQueryInterface(),
      logger: console,
    })
    // Example of direct migration list
    const {Umzug} = require('umzug')
    
    const umzug = new Umzug({
      migrations: [
        {
          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,
    })
  5. Run migrations using the CLI with ts-node

    main

    You can run migrations written in TypeScript by using a JavaScript entrypoint that registers ts-node. This allows you to load TypeScript modules directly without a separate compilation step.

    Available CLI commands for the migration script:

    • node migrate --help: Show CLI help
    • node migrate up: Apply migrations
    • node migrate down: Revert the last migration
    • node migrate down --to 0: Revert all migrations
    • node migrate up --step 2: Run only two migrations
    • node migrate create --name <name>.ts: Create a new migration file
    node migrate --help # show CLI help
    node migrate up # apply migrations
    node migrate down # revert the last migration
    node migrate down --to 0 # revert all migrations
    node migrate up --step 2 # run only two migrations
    
    node migrate create --name new-migration.ts # create a new migration file
  6. Run the Umzug bundling example

    main

    To run the provided bundling example using esbuild, follow these steps to install dependencies, generate the migration barrel, build the project, and execute migrations:

    # Install dependencies
    npm install
    
    # Update the migration barrel file
    npm run codegen
    
    # Build the project
    npm run build
    
    # Apply migrations
    node dist/umzug up
    
    # Create a new migration file
    node dist/umzug create --name new-migration.ts --skip-verify
    
    # Update the barrel again to include the new migration
    npm run codegen
    npm install
    npm run codegen
    npm run build
    node dist/umzug up
    node dist/umzug create --name new-migration.ts --skip-verify
    npm run codegen
  7. Use the Umzug CLI for migrations

    main

    The Umzug CLI allows you to manage migrations directly from the command line. In a vanilla Node.js setup, you can use the following commands to control the migration lifecycle:

    • Show help: View available CLI commands and flags.
    • Apply migrations: Run all pending migrations.
    • Revert migrations: Roll back the most recently applied migration.
    • Create a migration: Generate a new migration file with a specific name.
    • Revert all migrations: Roll back all applied migrations to the beginning (state 0).
    node migrate --help # show CLI help
    
    node migrate up # apply migrations
    node migrate down # revert the last migration
    node migrate create --name new-migration.js # create a new migration file
    
    node migrate up # apply migrations again
    node migrate down --to 0 # revert all migrations
  8. Use ECMAScript modules (ESM) with Umzug

    main

    To use ECMAScript modules with Umzug, you must provide a custom migrations.resolve function in the Umzug constructor. This function determines how migration files are loaded into the application.

    When implementing this:

    • Use import() to resolve .mjs migration files.
    • Use require() to resolve other formats (like .cjs).
    • Best Practice: Use explicit extensions like .mjs or .cjs instead of .js to avoid ambiguity in how the files are resolved.

    Depending on your environment and dependencies, you may or may not need to use createRequire to facilitate the resolution of CommonJS files within an ESM context.

  9. Run Umzug migrations via CLI

    main

    In this vanilla ESM setup, you can manage migrations using the migrate.mjs script via the Node.js CLI. The available commands allow you to view help, apply migrations, revert migrations, and generate new migration files.

    Commands:

    • node migrate.mjs --help: Display CLI help information.
    • node migrate.mjs up: Apply pending migrations.
    • node migrate.mjs down: Revert the most recent migration.
    • node migrate.mjs down --to 0: Revert all applied migrations.
    • node migrate.mjs create --name <filename>: Create a new migration file with the specified name.
    node migrate.mjs --help # show CLI help
    
    node migrate.mjs up # apply migrations
    node migrate.mjs down # revert the last migration
    node migrate.mjs create --name new-migration.mjs # create a new migration file
    
    node migrate.mjs up # apply migrations again
    node migrate.mjs down --to 0 # revert all migrations
  10. Use Umzug events for lifecycle hooks

    main

    Umzug emits events during the migration lifecycle that can be used to trigger side effects, such as shutting down internal services, logging, or notifying external systems. This allows you to hook into specific stages of the migration process (e.g., before or after migrations run).

    node migrate up
  11. Use a custom template for migration creation

    main

    By default, Umzug includes a basic template for creating new migrations. If you require a project-specific template (e.g., including specific imports, boilerplate, or file structures), you can implement a custom template resolver. This is typically achieved by reading a template file from a predefined folder on the filesystem during the migration creation process.

    node migrate create --name new-migration.ts