umzug
Framework-agnostic migration tool for Node.js with a clean, typed API for running and rolling back migrations.
Repository Health
Technical Analysis
Umzug is a framework-agnostic migration tool for Node.js, providing a small, typed API for running and rolling back tasks — commonly database schema changes, but not limited to them since migrations only receive whatever context object you configure. It ships with storage adapters for JSON files, in-memory state, MongoDB, and Sequelize, and supports declaring migrations as glob-matched files, a static array, or a function, with an optional CLI built on @rushstack/ts-command-line for teams that want a command-line migration runner without writing one from scratch.
Originally built alongside Sequelize (the current README still uses it in its primary example), Umzug 3.x decouples migration execution from any particular database or ORM: the context passed into every migration function can be a Sequelize QueryInterface, a raw SQL client, a MongoDB collection, or any other object, making it a general-purpose ‘apply and revert versioned changes’ primitive rather than a Sequelize-specific plugin.
What You Get
- A single Umzug class handling migration discovery, execution, and rollback via up(), down(), pending(), and executed().
- Built-in storage adapters for JSON files, in-memory tracking, MongoDB, and Sequelize, plus a documented UmzugStorage contract for writing your own.
- An optional CLI (runAsCLI()) that exposes the same commands as a command-line tool, built on @rushstack/ts-command-line.
- Migration scaffolding via create(), with configurable file templates (JS/TS/SQL) and confusing-order safeguards.
Common Use Cases
- Adding a versioned migration system to a Node.js app using Sequelize, raw SQL, or a custom data layer.
- Building an internal CLI for running and reverting schema changes without maintaining bespoke migration-runner code.
- Migrating non-relational stores (MongoDB, external APIs) using a context object instead of being locked into SQL-only tooling.
- Enforcing safe migration ordering and rerun semantics (skip vs. allow) in CI/CD pipelines before deploy.
Under The Hood
Architecture Umzug’s core class extends emittery (src/umzug.ts) and centers on a single Umzug<Ctx> class holding a pluggable storage: UmzugStorage<Ctx> and a migrations resolver function computed once in the constructor via getMigrationsResolver, which normalizes three input shapes (a static array, a lazy function, or a {glob, resolve} descriptor) into one (ctx) => Promise<RunnableMigration[]> signature. All public commands (up, down, pending, executed, create) route through a shared runCommand wrapper that resolves the context (a value, or a sync/async factory) and emits beforeCommand/afterCommand events around the callback, giving every operation consistent instrumentation with almost no duplicated glue. Storage is abstracted behind the UmzugStorage interface (src/storage/contract.ts) with concrete implementations (json.ts, memory.ts, mongodb.ts, sequelize.ts) selected by the caller, so the migration-tracking data model can change without touching the core up/down logic. The optional CLI (src/cli.ts, UmzugCLI) is a thin adapter calling the same public methods a programmatic consumer would use, so runAsCLI() doesn’t fork behavior from library usage. Replacing the resolver or the storage implementation is the well-supported extension seam; changing the shared runCommand/event-emission behavior would ripple through every command.
Tech Stack Written in TypeScript targeting ES2019/CommonJS (tsconfig.json), compiled with tsc via a plain compile/build npm script and published as lib/ per package.json’s files field. Runtime dependencies are deliberately minimal: emittery (typed event emitter the core class extends), pony-cause (error-cause wrapping for MigrationError), tinyglobby (glob resolution for file-based migrations), type-fest (utility types like MergeExclusive), and @rushstack/ts-command-line (backing the optional CLI). It has no required database dependency — sequelize and sqlite3 appear only in devDependencies to exercise the bundled SequelizeStorage in tests. Test tooling is Vitest with @vitest/coverage-v8, linting is eslint-plugin-mmkal’s flat config, and np/pkg-pr-new handle release and preview-package publishing. Package management is pnpm (version pinned via packageManager), with Renovate configured for dependency updates.
Code Quality Tests live in test/ mirroring the src/ layout (umzug.test.ts, cli.test.ts, storage/, lock.test.ts, sequelize.test.ts, examples.test.ts) and run under Vitest with snapshot testing and code coverage, exercised on both Linux and Windows in CI plus a separate job that installs the built tarball across five Node versions to catch packaging/compatibility regressions. Error handling is explicit and typed: MigrationError extends pony-cause’s ErrorWithCause to preserve the original thrown error as a cause while attaching structured migration/direction context, and a dedicated MissingResolverError guards migration file types with no resolver. strictNullChecks, noImplicitAny, and noImplicitReturns are enabled in tsconfig.json, and the lint step runs with —max-warnings 0 as a required CI gate. Naming is consistent and the public surface is small, with TSDoc comments directly on exported members.
API Design Umzug’s public API is deliberately small — a single Umzug class with up/down/pending/executed/create — while still supporting three different ways to declare migrations (glob, static array, or resolver function) without multiplying the API surface. The _types.migration compile-time-only property is a distinctive ergonomic trick: it lets a project export a fully-typed Migration alias derived from its own Umzug instance so individual migration files get correct context typing without manual generic wiring. Storage is injected rather than baked in, so switching from the default JSONStorage to SequelizeStorage or a custom UmzugStorage implementation requires no changes to migration files themselves. Getting started requires little boilerplate — the README’s minimal example is a short Umzug construction plus a two-function migration file — and the emitted lifecycle events (migrating, migrated, reverting, reverted) give structured hooks for logging without requiring a custom subclass.
Used by 6 apps in this directory
ChartBrew
Analytics · Databases
Open-source reporting platform to build live dashboards from SQL, NoSQL, APIs, and SaaS tools with an AI assistant that creates charts from natural language.
NocoBase
No Code Platforms · Low Code Platforms
Open-source AI + no-code platform that lets coding agents and people collaborate to build business systems fast on proven infrastructure.
Outline
Knowledge Management · Collaboration
A fast, real-time collaborative knowledge base for growing teams built on React, Node.js, and ProseMirror.
SerpBear
Marketing · Analytics
Self-hosted keyword rank tracker with Google Search Console integration, multi-provider SERP scraping, and a built-in REST API.
strapi
CMS
Open-source headless CMS that auto-generates REST and GraphQL APIs from your content models, with a fully customizable admin panel you control.
Webstudio
Design Tools · No Code Platforms
Open source visual development platform with complete CSS control, headless CMS integration, and full infrastructure ownership