@danieljanocha/mongogrator
v1.3.1
Published
Mongogrator is a lightweight typescript-based package for MongoDB database migrations
Maintainers
Readme
Forked from tepinly/mongogrator — original work by @tepinly.
Mongogrator is a very fast database migration CLI for MongoDB. Its purpose is to easily create and run migrations for development and production stages
Installing
pnpm add @danieljanocha/mongogrator
# or: npm i @danieljanocha/mongogrator
# or: bun add @danieljanocha/mongogratorThe CLI binary is exposed as mongogrator (run via pnpm exec mongogrator, npx mongogrator, or bunx mongogrator).
Standalone binary (upstream)
The upstream project also ships a one-line installer that downloads a prebuilt binary:
# MacOS/Linux
curl -fsSL git.new/mongogrator-installer.sh | bash
# Windows
cmd /c "curl -L https://git.new/mongogrator-installer.ps1 | powershell -c -"List of commands
Mongogrator CLI
Usage: mongogrator <command> [options]
Commands:
init [--js] Initialize a new configuration file
add [--config <path>] Creates a new migration file with the provided name
list [--config <path>] List all migrations and their status
apply, migrate Run all migrations that have not been applied yet
(alias: migrate; accepts [--config <path>])
version, -v, --version Prints the current version of Mongogrator
Flags:
--help, -h Prints the detailed description of the command
--config <path> Use a custom mongogrator config file
Environment variables:
MONGOGRATOR_CONFIG_PATH Equivalent of --config (--config wins if both are set)
NO_COLOR Set to any value to disable colored log outputLog output is colorized by default (blue for info, red for errors) so
Mongogrator lines stand out in CI build logs. Set NO_COLOR to any value to
force plain, uncolored output (see no-color.org).
Usage guide
A basic guide on how to use the CLI
Adding new migrations
Start by initializing the config file
mongogrator initThis initializes a mongogrator.config.ts file in the location of the command. You can optionally pass a --js flag at the end of the command to initialize in a js file
Setup the url to the desired mongo cluster, and make sure it's running
mongogrator add insert_userThis will create the migration file under the directory key assigned in the config migrationsPath
The following is an example of a newly created ts migration file
import { buildMigration } from '@danieljanocha/mongogrator'
export default buildMigration({
migrate: async ({ db, client }) => {
// Migration code here
},
})The migrations are executed through the native MongoDB Node.js driver. Each migration receives both the default db (resolved from config.database) and the underlying client (MongoClient) so you can reach additional databases when needed.
Migration query example
import { buildMigration } from '@danieljanocha/mongogrator'
export default buildMigration({
migrate: async ({ db }) => {
await db.collection('users').insertOne({ name: 'Alex' })
},
})Multi-database migrations
If you run several databases off the same cluster (e.g. one for auth + one per app client), use client to address them explicitly:
import { buildMigration } from '@danieljanocha/mongogrator'
export default buildMigration({
migrate: async ({ client }) => {
const authDb = client.db('auth')
const twitchDb = client.db('twitch')
const facebookDb = client.db('facebook')
await authDb.collection('users').createIndex({ email: 1 }, { unique: true })
for (const appDb of [twitchDb, facebookDb]) {
await appDb.collection('sessions').createIndex({ createdAt: 1 })
}
},
})Migrations list
You can add as many migrations as you want and then call the list command to display the status of each
mongogrator listThis will print out a list of all the migrations, each has a status of either NOT MIGRATED or MIGRATED
┌───┬───────────────────────────────┬──────────────┐
│ │ migration │ status │
├───┼───────────────────────────────┼──────────────┤
│ 0 │ 20240923150201806_insert_user │ NOT MIGRATED │
└───┴───────────────────────────────┴──────────────┘Naturally, the status will be NOT MIGRATED as we haven't run the migration yet
Running the migrations
Run the migrations simply by calling
mongogrator applyThis will run all the migrations and log them to the database under the specified collection name in the config logsCollectionName
For production purposes, you can pass the config file path to the apply command directly via --config if the config isn't in the current working directory
mongogrator apply --config ./dist/mongogrator.config.jsAlternatively, set the MONGOGRATOR_CONFIG_PATH environment variable. This is handy when wiring a single pnpm migrate script that forwards arbitrary subcommands:
// package.json
{
"scripts": {
"migrate": "MONGOGRATOR_CONFIG_PATH=./src/migrations/mongogrator.config.ts mongogrator"
}
}pnpm migrate apply # → mongogrator apply (reads MONGOGRATOR_CONFIG_PATH)
pnpm migrate list # → mongogrator list
pnpm migrate add foo # → mongogrator add fooIf both are provided, --config wins over the env var. In either case, migrationsPath is resolved relative to the config file's directory.
Now if you run the list command again, it will reveal that the migration file has been successfully executed
┌───┬───────────────────────────────┬──────────────┐
│ │ migration │ status │
├───┼───────────────────────────────┼──────────────┤
│ 0 │ 20240923150201806_insert_user │ MIGRATED │
└───┴───────────────────────────────┴──────────────┘Logs collection schema
{
_id: objectId(),
name: string,
createdAt: Date(),
}By default _id is an auto-generated MongoDB ObjectId. If you'd rather use a different id type (e.g. a plain string UUID), pass a generateId function in the config — see Custom _id in the logs collection below.
Configuration
{
url: 'mongodb://localhost:27017', // Cluster url
database: 'test', // Database name for which the migrations will be executed
migrationsPath: './migrations', // Migrations directory relative to the location of the config file
logsCollectionName: 'migrations', // Name of the logs collection that will be stored in the database
format: 'ts', // Format type of the migration files ['ts', 'js']
callbacksBeforeMigrations: [], // Async hooks ({ db, client }) => Promise<void>, run once before the batch
callbacksAfterMigrations: [], // Async hooks ({ db, client }) => Promise<void>, run once after the batch
generateId: () => crypto.randomUUID(), // Optional. Sets the _id of every row inserted into the logs collection. Omit to let MongoDB auto-generate an ObjectId.
}Custom _id in the logs collection
By default Mongogrator does not set _id on the rows it writes to the logs collection (logsCollectionName), so the MongoDB driver auto-generates a fresh ObjectId per insert. If you'd rather store a different id type — for example a plain string UUID so the field is easier to read or copy from a GUI — pass a generateId function in the config:
import { buildMongogratorConfig } from '@danieljanocha/mongogrator'
import { randomUUID } from 'node:crypto'
export default buildMongogratorConfig({
url: 'mongodb://localhost:27017',
database: 'test',
migrationsPath: './migrations',
logsCollectionName: 'migrations',
format: 'ts',
generateId: () => randomUUID(),
})Notes:
generateIdis called once per applied migration, and its return value is written as that row's_id.- It affects only the logs collection. Documents your own migrations insert are not touched.
- Return whatever BSON-compatible type you want — a string (e.g.
randomUUID()), anObjectId, a number, etc. - Omit the field to keep the default behavior (auto-generated
ObjectId).
For a type-safe config, use the buildMongogratorConfig builder. It validates the shape at load time and gives you autocomplete on the input:
// mongogrator.config.ts
import { buildMongogratorConfig } from '@danieljanocha/mongogrator'
export default buildMongogratorConfig({
url: 'mongodb://localhost:27017',
database: 'test',
migrationsPath: './migrations',
logsCollectionName: 'migrations',
format: 'ts',
})Migration files use the same builder pattern with a forced default export — this prevents typos like accidentally exporting migratee instead of migrate:
// migrations/20240923150201806_insert_user.ts
import { buildMigration } from '@danieljanocha/mongogrator'
export default buildMigration({
migrate: async ({ db }) => {
await db.collection('users').insertOne({ name: 'Alex' })
},
})[!IMPORTANT] all the config keys with path values are relative to the location of the config file itself
