Skip to content

Configuration ​

Step 3 of the learning path · about 10 minutes

mmk needs two things to run: a connection string and a database name. Everything else has a default. You can set things in four places. When the same setting appears in more than one, the higher one wins:

  1. Command-line flags, like --uri and --db
  2. Environment variables, like MMK_URI and MMK_DB (a .env file is read automatically)
  3. A config file, mmk.config.js / .ts / .json
  4. Built-in defaults

A config file is optional. In CI it's common to have none at all and set two environment variables.

The config file ​

mmk init writes a config file with every option and a comment explaining each one. mmk looks for these in the folder you run it from, and uses the first it finds:

  1. mmk.config.ts
  2. mmk.config.js
  3. mmk.config.json

To use a file somewhere else, pass --config path/to/file.

js
export default {
  uri: process.env.MONGO_URL ?? 'mongodb://localhost:27017',
  dbName: 'shop',
  migrationsDir: './migrations',
};
ts
import type { MmkConfig } from 'mongo-migrate-kit';

const config: Partial<MmkConfig> = {
  uri: process.env.MONGO_URL ?? 'mongodb://localhost:27017',
  dbName: 'shop',
  migrationsDir: './migrations',
  createExtension: 'ts',
};

export default config;
json
{
  "uri": "mongodb://localhost:27017",
  "dbName": "shop",
  "migrationsDir": "./migrations"
}

These examples use export default. In a CommonJS project (no "type": "module" in package.json) write module.exports = instead. mmk init picks the right one for you. A .ts config needs Node 22.18 or newer, or tsx (see TypeScript).

A JSON config can't hold functions, so hooks, logger and mongoose only work in a .js or .ts file.

Loading the connection string from a secret manager ​

A .js or .ts config can export a function instead of an object. It can be async, so you can fetch the connection string from AWS Secrets Manager, Google Secret Manager, Vault or anything else when mmk starts:

js
// mmk.config.js
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';

export default async () => {
  const sm = new SecretsManagerClient({ region: 'eu-west-1' });
  const res = await sm.send(new GetSecretValueCommand({ SecretId: 'prod/mongo' }));
  const { uri, dbName } = JSON.parse(res.SecretString ?? '{}');
  return { uri, dbName, migrationsDir: './migrations' };
};

mmk doesn't ship any cloud SDKs. You install the one you use. The secret is never written to disk, and a rotated secret is picked up on the next run. mmk init --secret-provider writes a starting point for you. If the function throws, mmk stops with CONFIG_INVALID and shows the original error.

Values returned this way sit at the config-file level, so environment variables and flags still override them.

All settings ​

SettingDefaultWhat it does
urinone, requiredMongoDB connection string. Must start with mongodb:// or mongodb+srv://.
dbNamenone, requiredThe database to migrate.
migrationsDir'./migrations'Folder with your migration files, relative to where you run mmk.
migrationsCollection'_mmk_migrations'Collection that holds the history.
lockCollection'_mmk_locks'Collection that holds the lock.
lockTTLSeconds60How long before an abandoned lock counts as stale.
strictfalseStop instead of warn when you name an applied file whose checksum changed.
useTransactionfalseRun every migration in a transaction. A file's own useTransaction overrides this.
fileExtensions['.ts', '.js']Which files in migrationsDir count as migrations.
createExtension'js'File type mmk create writes: 'js' or 'ts'.
sequentialfalseName new files 0001-, 0002- instead of using a timestamp.
templatePathnoneYour own template for mmk create.
authoryour git name, then OS userWhat mmk create writes into the author line.
requireAuthorfalseRefuse mmk up if a file to run has no author.
namespacenoneOnly for several repos on one database. Lowercase letters, digits, . _ -, up to 64 characters. Set once and never change it.
mongoClientOptionsnoneExtra options for the MongoDB client: TLS, timeouts, auth, read preference.
hooksnoneFunctions that run around migrations. See Hooks.
mongoosenoneYour Mongoose instance, if migrations use it. It shows up as ctx.mongoose.
loggerbuilt-inYour own logger, or null to print nothing (handy in tests).
servicenearest package.json nameDeprecated. Use namespace. Will be removed in 2.0.

Environment variables ​

Every simple setting has a variable. They override the config file.

VariableSetting
MMK_URIuri
MMK_DBdbName
MMK_MIGRATIONS_DIRmigrationsDir
MMK_COLLECTIONmigrationsCollection
MMK_LOCK_COLLECTIONlockCollection
MMK_LOCK_TTLlockTTLSeconds
MMK_STRICTstrict
MMK_USE_TRANSACTIONuseTransaction
MMK_SEQUENTIALsequential
MMK_CREATE_EXTENSIONcreateExtension
MMK_AUTHORauthor
MMK_REQUIRE_AUTHORrequireAuthor
MMK_NAMESPACEnamespace. Prefer the config file for this one, so a shared CI runner can't give every repo the same value.
MMK_SERVICEservice (deprecated)

True/false settings accept true, 1, yes, y, on and false, 0, no, n, off. An empty value (MMK_STRICT=) counts as not set, so you can blank a line in .env without deleting it.

Command-line flags ​

These work on every command and beat everything else:

FlagOverrides
--uri <uri>MMK_URI and uri
--db <name>MMK_DB and dbName
--dir <path>MMK_MIGRATIONS_DIR and migrationsDir
--config <path>which config file to load
bash
npx mmk up --uri "mongodb://localhost:27017" --db shop --dir ./db/migrations

To see which layer won for each setting, run mmk config. It prints every resolved value and where it came from, without connecting.

When a setting is wrong ​

mmk checks all settings before it connects, and reports every problem at once instead of one per run. For each one it tells you the setting, the value it read, where it read it from, and every way to set it:

Terminal: MMK_LOCK_TTL=abc npx mmk up prints CONFIG_INVALID with two numbered problems: lockTTLSeconds must be a whole number, received abc, read from MMK_LOCK_TTL; and uri is required but was not set

It also refuses to guess. These are all errors (or warnings) rather than being quietly ignored:

MistakeWhat you get
uri: 'localhost:27017'It must start with mongodb:// or mongodb+srv://
A dbName with / \ . " $ * < > : | ? or a spaceRejected before the driver sees it
MMK_STRICT=maybe, MMK_LOCK_TTL=abc, MMK_CREATE_EXTENSION=pyNames the variable, the value and what's accepted
migrationDir (missing an "s") in a config filerename it to "migrationsDir"
MMK_MIGRATION_DIR in the environmentA warning: did you mean MMK_MIGRATIONS_DIR?
fileExtensions: ['ts']Extensions must start with a dot
strict: 'true' (a string)It has to be a real true or false
hooks: { beforeALL }Did you mean beforeAll?
A logger missing some methodsLists the missing ones
migrationsDir points at nothingA warning with the full path it looked at

With --json, the same problems come back as data on stdout, under error.details.issues:

json
{
  "error": {
    "code": "CONFIG_INVALID",
    "message": "Invalid mongo-migrate-kit configuration — 1 problem found: …",
    "details": {
      "issues": [{ "key": "uri", "problem": "is required but was not set", "howToSet": "…" }]
    }
  }
}

In code, they're on error.context.issues:

ts
import { runMigrations, ConfigInvalidError, type ConfigIssue } from 'mongo-migrate-kit';

try {
  await runMigrations({ uri: process.env.MONGO_URL, dbName: 'shop' });
} catch (error) {
  if (error instanceof ConfigInvalidError) {
    for (const issue of (error.context?.issues ?? []) as ConfigIssue[]) {
      console.error(issue.key, issue.problem, issue.source, issue.howToSet);
    }
  }
  throw error;
}

Connection defaults ​

mmk creates the MongoDB client with a few defaults so a wrong address fails in seconds instead of hanging:

  • serverSelectionTimeoutMS and connectTimeoutMS are 10 seconds.
  • retryWrites is on.
  • Your write concern is left alone. If your URI says w=1 for a fast bulk migration, that's what your migration gets.

The only writes mmk forces to w: 'majority' are its own: the history and the lock. That way the history and the lock survive a primary failover whatever your URI says.

Anything in mongoClientOptions is applied last and overrides these defaults:

js
export default {
  uri: process.env.MONGO_URL,
  dbName: 'shop',
  mongoClientOptions: {
    tls: true,
    serverSelectionTimeoutMS: 5000,
    readPreference: 'primary',
  },
};

Passwords in error messages

The MongoDB driver sometimes puts the connection string in its error messages. mmk removes user:password@ from those before they reach your logs, --json output or the error object.

Next ​

Deploying and CI shows how to run migrations in a pipeline, block a deploy while migrations are pending, and read the JSON output.

Released under the MIT License.