Moving from migrate-mongo
If your project already uses migrate-mongo, you don't start over. mmk import reads migrate-mongo's changelog collection and writes the same history into mmk's own collection. After that, mmk up knows what already ran and only runs new files.
Your old changelog collection is only read, never changed. If you decide mmk isn't for you, delete the _mmk_migrations collection and you're back where you started.
The switch, step by step
1. Install mmk next to migrate-mongo. You can remove migrate-mongo later.
npm install mongo-migrate-kit2. Point it at the same database and folder. If your migrations live in migrations/, that's already the default. Otherwise set migrationsDir:
// mmk.config.js
export default {
uri: process.env.MONGO_URL,
dbName: 'shop',
migrationsDir: './migrations',
};3. Preview the import. This writes nothing:
npx mmk import --dry-run◎ Dry-run Would import 3 record(s) from "changelog" → "_mmk_migrations"
┌─────────────────────────────────┬───────┬─────────────────────┬────────────┐
│ Migration │ Batch │ Applied At │ Checksum │
├─────────────────────────────────┼───────┼─────────────────────┼────────────┤
│ 20250301100000-create-users.js │ 1 │ 2025-03-01 15:35:00 │ recomputed │
├─────────────────────────────────┼───────┼─────────────────────┼────────────┤
│ 20250412083000-add-sku-index.js │ 2 │ 2025-04-12 14:10:00 │ recomputed │
├─────────────────────────────────┼───────┼─────────────────────┼────────────┤
│ 20250720140000-split-address.js │ 3 │ 2025-07-20 19:40:00 │ recomputed │
└─────────────────────────────────┴───────┴─────────────────────┴────────────┘Check that the list matches what you expect has run.
4. Import.
npx mmk import5. Carry on as usual. npx mmk status shows the old migrations as applied. npx mmk up runs only files that migrate-mongo never ran.
6. Update your scripts and CI to call mmk instead of migrate-mongo. Do this in every environment (staging, production) at the same time, so the two tools never both run against one database.
What import does exactly
- Reads every document in the source collection (
changelog, or--from <name>). - For each one, writes a record to
_mmk_migrations(or--to <name>) with the file name and the date it was applied. - Works out a checksum for each file. If migrate-mongo stored a
fileHashand it matches the file on disk, that's reused. Otherwise it's computed from the file. If the file is gone, the checksum is left empty and you get a warning.--trust-hashreuses migrate-mongo's hash without checking. - Gives each imported migration its own batch number, in the order they ran.
- Leaves files that aren't in the old changelog alone. They're pending and run on the next
mmk up.
If _mmk_migrations already has records, import stops with IMPORT_TARGET_NOT_EMPTY. Add --force to import anyway; the new batch numbers continue after the existing ones.
Old migrations are forward-only
There's one difference to know about. migrate-mongo calls up(db, client) with two arguments. mmk calls up({ db, client }) with one object. Running an old migrate-mongo file through mmk could silently do the wrong thing, so mmk doesn't try. mmk down and mmk redo refuse imported records before touching anything:
✖ MIGRATION_IRREVERSIBLE: Cannot roll back migrate-mongo-imported migration(s): 20250101000000-legacy.jsNew migrations you write with mmk create roll back normally. If you need an old one to be reversible, rewrite its file in mmk's format:
module.exports = {
async up(db, client) {
await db.collection('users').createIndex({ email: 1 }, { unique: true });
},
async down(db, client) {
await db.collection('users').dropIndex('email_1');
},
};module.exports = {
async up({ db, client }) {
await db.collection('users').createIndex({ email: 1 }, { unique: true });
},
async down({ db, client }) {
await db.collection('users').dropIndex('email_1');
},
};The only change is the braces around { db, client }.
Several services sharing one changelog
If several repos used to share one migrate-mongo changelog, set a namespace in each repo and run mmk import in each. A repo only imports records whose file is in its own migrations folder, and stamps them with its namespace. Records that belong to other repos are listed as skipped.
What you get after switching
| Capability | migrate-mongo | mongo-migrate-kit |
|---|---|---|
| Run a single migration file | ❌ | ✅ |
| Roll back a specific batch (not just the last) | ❌ | ✅ |
| Roll back a single migration file | ❌ | ✅ |
| Dry-run preview | ❌ | ✅ |
redo (down + up) | ❌ | ✅ |
| Concurrency lock on by default | ❌ | ✅ |
| Refuses to roll back an edited migration | ❌ | ✅ |
| Lifecycle hooks | ❌ | ✅ |
| History kept on rollback (never deleted) | ❌ | ✅ |
Adopt an existing migrate-mongo changelog | — | ✅ |
Reflects migrate-mongo's documented CLI as of mid-2026. It also gives migrations transaction access through a client argument; mmk exposes the same plus a declarative per-file useTransaction flag.
The full command reference is at mmk import.
Next
The FAQ covers the questions people ask most, from Mongoose to multiple databases.