Skip to main content
Version: 5.0

Configuration

Entity Discovery

You can either provide array of entity instances via entities, or let the ORM look up your entities in selected folders.

MikroORM.init({
entities: [Author, Book, Publisher, BookTag],
});

We can also use folder based discovery by providing list of paths to the entities we want to discover (globs are supported as well). This way we also need to specify entitiesTs, where we point the paths to the TS source files instead of the JS compiled files (see more at Metadata Providers).

The entitiesTs option is used when running the app via ts-node, as the ORM needs to discover the TS files. Always specify this option if you use folder/file based discovery.

MikroORM.init({
entities: ['./dist/modules/users/entities', './dist/modules/projects/entities'],
entitiesTs: ['./src/modules/users/entities', './src/modules/projects/entities'],
// optionally you can override the base directory (defaults to `process.cwd()`)
baseDir: process.cwd(),
});

Be careful when overriding the baseDir with dynamic values like __dirname, as you can end up with valid paths from ts-node, but invalid paths from node. Ideally you should keep the default of process.cwd() there to always have the same base path regardless of how you run the app.

By default, ReflectMetadataProvider is used that leverages the reflect-metadata. You can also use TsMorphMetadataProvider by installing @mikro-orm/reflection. This provider will analyse your entity source files (or .d.ts type definition files). If you aim to use plain JavaScript instead of TypeScript, use EntitySchema or the JavaScriptMetadataProvider.

You can also implement your own metadata provider and use it instead. To do so, extend the MetadataProvider class.

import { MikroORM } from '@mikro-orm/core';
import { TsMorphMetadataProvider } from '@mikro-orm/reflection';

MikroORM.init({
metadataProvider: TsMorphMetadataProvider,
});

There are also some additional options how you can adjust the discovery process:

MikroORM.init({
discovery: {
warnWhenNoEntities: false, // by default, discovery throws when no entity is processed
requireEntitiesArray: true, // force usage of class references in `entities` instead of paths
alwaysAnalyseProperties: false, // do not analyse properties when not needed (with ts-morph)
},
});

If you disable discovery.alwaysAnalyseProperties option, you will need to explicitly provide nullable and wrappedReference parameters (where applicable).

Read more about this in Metadata Providers sections.

Driver

To select driver, you can either use type option, or provide the driver class reference.

typedriver namedependencynote
mongoMongoDrivermongodb^3.3.4-
mysqlMySqlDrivermysql2^2.0.0compatible with MariaDB
mariadbMariaDbDrivermariadb^2.0.0compatible with MySQL
postgresqlPostgreSqlDriverpg^7.0.0-
sqliteSqliteDriversqlite3^4.0.0-

Driver and connection implementations are not directly exported from @mikro-orm/core module. You can import them from the driver packages (e.g. import { PostgreSqlDriver } from '@mikro-orm/postgresql').

You can pass additional options to the underlying driver (e.g. mysql2) via driverOptions. The object will be deeply merged, overriding all internally used options.

import { MySqlDriver } from '@mikro-orm/mysql';

MikroORM.init({
driver: MySqlDriver,
driverOptions: { connection: { timezone: '+02:00' } },
});

From v3.5.1 you can also set the timezone directly in the ORM configuration:

MikroORM.init({
type: 'mysql',
timezone: '+02:00',
});

Connection

Each platform (driver) provides default connection string, you can override it as a whole through clientUrl, or partially through one of following options:

export interface DynamicPassword {
password: string;
expirationChecker?: () => boolean;
}

export interface ConnectionOptions {
dbName?: string;
name?: string; // for logging only (when replicas are used)
clientUrl?: string;
host?: string;
port?: number;
user?: string;
password?: string | (() => string | Promise<string> | DynamicPassword | Promise<DynamicPassword>);
charset?: string;
multipleStatements?: boolean; // for mysql driver
pool?: PoolConfig; // provided by `knex`
}

Following table shows default client connection strings:

typedefault connection url
mongomongodb://127.0.0.1:27017
mysqlmysql://root@127.0.0.1:3306
mariadbmysql://root@127.0.0.1:3306
postgresqlpostgresql://postgres@127.0.0.1:5432

Read Replicas

To set up read replicas, you can use replicas option. You can provide only those parts of the ConnectionOptions interface, they will be used to override the master connection options.

MikroORM.init({
type: 'mysql',
dbName: 'my_db_name',
user: 'write-user',
host: 'master.db.example.com',
port: 3306,
replicas: [
{ user: 'read-user-1', host: 'read-1.db.example.com', port: 3307 },
{ user: 'read-user-2', host: 'read-2.db.example.com', port: 3308 },
{ user: 'read-user-3', host: 'read-3.db.example.com', port: 3309 },
],
});

Read more about this in Installation and Read Connections sections.

Using short-lived tokens

Many cloud providers include alternative methods for connecting to database instances using short-lived authentication tokens. MikroORM supports dynamic passwords via a callback function, either synchronous or asynchronous. The callback function must resolve to a string.

MikroORM.init({
type: 'mysql',
dbName: 'my_db_name',
password: async () => someCallToGetTheToken(),
});

The password callback value will be cached, to invalidate this cache we can specify expirationChecker callback:

MikroORM.init({
type: 'mysql',
dbName: 'my_db_name',
password: async () => {
const { token, tokenExpiration } = await someCallToGetTheToken();
return { password: token, expirationChecker: () => tokenExpiration <= Date.now() }
},
});

Naming Strategy

When mapping your entities to database tables and columns, their names will be defined by naming strategy. There are 3 basic naming strategies you can choose from:

  • UnderscoreNamingStrategy - default of all SQL drivers
  • MongoNamingStrategy - default of MongoDriver
  • EntityCaseNamingStrategy - uses unchanged entity and property names

You can also define your own custom NamingStrategy implementation.

MikroORM.init({
namingStrategy: EntityCaseNamingStrategy,
});

Read more about this in Naming Strategy section.

Auto-join of 1:1 owners

By default, owning side of 1:1 relation will be auto-joined when you select the inverse side so we can have the reference to it. You can disable this behaviour via autoJoinOneToOneOwner configuration toggle.

MikroORM.init({
autoJoinOneToOneOwner: false,
});

Propagation of 1:1 and m:1 owners

MikroORM defines getter and setter for every owning side of m:1 and 1:1 relation. This is then used for propagation of changes to the inverse side of bi-directional relations.

const author = new Author('n', 'e');
const book = new Book('t');
book.author = author;
console.log(author.books.contains(book)); // true

You can disable this behaviour via propagateToOneOwner option.

MikroORM.init({
propagateToOneOwner: false,
});

Forcing UTC Timezone

Use forceUtcTimezone option to force the Dates to be saved in UTC in datetime columns without timezone. It works for MySQL (datetime type) and PostgreSQL (timestamp type). SQLite does this by default.

MikroORM.init({
forceUtcTimezone: true,
});

Mapping null values to undefined

By default null values from nullable database columns are hydrated as null. Using forceUndefined we can tell the ORM to convert those null values to undefined instead.

MikroORM.init({
forceUndefined: true,
});

Serialization of new entities

After flushing a new entity, all relations are marked as populated, just like if the entity was loaded from the db. This aligns the serialized output of e.toJSON() of a loaded entity and just-inserted one.

In v4 this behaviour was disabled by default, so even after the new entity was flushed, the serialized form contained only FKs for its relations. We can opt in to this old behaviour via populateAfterFlush: false.

MikroORM.init({
populateAfterFlush: false,
});

Population where condition

This applies only to SELECT_IN strategy, as JOINED strategy implies the inference.

In v4, when we used populate hints in em.find() and similar methods, the query for our entity would be analysed and parts of it extracted and used for the population. Following example would find all authors that have books with given IDs, and populate their books collection, again using this PK condition, resulting in only such books being in those collections.

// this would end up with `Author.books` collections having only books of PK 1, 2, 3
const a = await em.find(Author, { books: [1, 2, 3] }, { populate: ['books'] });

Following this example, if we wanted to load all books, we would need a separate em.populate() call:

const a = await em.find(Author, { books: [1, 2, 3] });
await em.populate(a, ['books']);

This behaviour changed and is now configurable both globally and locally, via populateWhere option. Globally we can specify one of PopulateHint.ALL and PopulateHint.INFER, the former being the default in v5, the latter being the default behaviour in v4. Locally (via FindOptions) we can also specify custom where condition that will be passed to em.populate() call.

MikroORM.init({
// defaults to PopulateHint.ALL in v5
populateWhere: PopulateHint.INFER, // revert to v4 behaviour
});

Custom Hydrator

Hydrator is responsible for assigning values from the database to entities. You can implement your custom Hydrator (by extending the abstract Hydrator class):

MikroORM.init({
hydrator: MyCustomHydrator,
});

Custom Repository

You can also register custom base repository (for all entities where you do not specify customRepository) globally:

You can still use entity specific repositories in combination with global base repository.

MikroORM.init({
entityRepository: CustomBaseRepository,
});

Read more about this in Repositories section.

Strict Mode and property validation

Since v4.0.3 the validation needs to be explicitly enabled via validate: true. It has performance implications and usually should not be needed, as long as you don't modify your entities via Object.assign().

MikroORM will validate your properties before actual persisting happens. It will try to fix wrong data types for you automatically. If automatic conversion fails, it will throw an error. You can enable strict mode to disable this feature and let ORM throw errors instead. Validation is triggered when persisting the entity.

MikroORM.init({
validate: true,
strict: true,
});

Read more about this in Property Validation section.

Required properties validation

Since v5, new entities are validated on runtime (just before executing insert queries), based on the entity metadata. This means that mongo users now need to use nullable: true on their optional properties too).

This behaviour can be disabled globally via validateRequired: false in the ORM config.

MikroORM.init({
validateRequired: false,
});

Debugging & Logging

You can enable logging with debug option. Either set it to true to log everything, or provide array of 'query' | 'query-params' | 'discovery' | 'info' namespaces.

MikroORM.init({
logger: (message: string) => myLogger.info(message), // defaults to `console.log()`
debug: true, // or provide array like `['query', 'query-params']`
highlight: false, // defaults to true
highlightTheme: { ... }, // you can also provide custom highlight there
});

Read more about this in Debugging section.

Custom Fail Handler

When no entity is found during em.findOneOrFail() call, new Error() will be thrown. You can customize how the Error instance is created via findOneOrFailHandler:

MikroORM.init({
findOneOrFailHandler: (entityName: string, where: Dictionary | IPrimaryKey) => {
return new NotFoundException(`${entityName} not found!`);
},
});

Read more about this in Entity Manager docs.

Schema Generator

Following example shows all possible options and their defaults:

MikroORM.init({
schemaGenerator: {
disableForeignKeys: true, // try to disable foreign_key_checks (or equivalent)
createForeignKeyConstraints: true, // do not generate FK constraints
},
});

Migrations

Under the migrations namespace, you can adjust how the integrated migrations support works. Following example shows all possible options and their defaults:

MikroORM.init({
migrations: {
tableName: 'mikro_orm_migrations', // migrations table name
path: process.cwd() + '/migrations', // path to folder with migration files
glob: '!(*.d).{js,ts}', // how to match migration files (all .js and .ts files, but not .d.ts)
transactional: true, // run each migration inside transaction
disableForeignKeys: true, // try to disable foreign_key_checks (or equivalent)
allOrNothing: true, // run all migrations in current batch in master transaction
emit: 'ts', // migration generation mode
},
});

Read more about this in Migrations section.

Seeder

Following example shows all possible options and their defaults:

MikroORM.init({
seeder: {
path: './seeders',
defaultSeeder: 'DatabaseSeeder',
},
});

Read more about this in seeding docs.

Caching

By default, metadata discovery results are cached. You can either disable caching, or adjust how it works. Following example shows all possible options and their defaults:

MikroORM.init({
cache: {
enabled: true,
pretty: false, // allows to pretty print the JSON cache
adapter: FileCacheAdapter, // you can provide your own implementation here, e.g. with redis
options: { cacheDir: process.cwd() + '/temp' }, // options will be passed to the constructor of `adapter` class
},
});

Read more about this in Metadata Cache section.

Importing database dump files (MySQL and PostgreSQL)

Using the mikro-orm database:import db-file.sql you can import a database dump file. This can be useful when kickstarting an application or could be used in tests to reset the database. Database dumps often have queries spread over multiple lines and therefore you need the following configuration.

MikroORM.init({
...
multipleStatements: true,
...
});

This should be disabled in production environments for added security.

Using native private properties

If we want to use native private properties inside entities, the default approach of how MikroORM creates entity instances via Object.create() is not viable (more about this in the issue). To force usage of entity constructors, we can use forceEntityConstructor toggle:

MikroORM.init({
forceEntityConstructor: true, // or specify just some entities via `[Author, 'Book', ...]`
});

Persist created entities automatically

If we create new entity via em.create(), we still need to mark it via em.persist() to make the EntityManager aware of it. Alternatively we can enable persistOnCreate flag, which will make this work automatically.

This flag affects only em.create(), entities created via constructors still need explicit em.persist() call or they need to be part of entity graph of some already managed entity.

MikroORM.init({
persistOnCreate: true,
});

Using global Identity Map

In v5, it is no longer possible to use the global identity map. This was a common issue that led to weird bugs, as using the global EM without request context is almost always wrong, we always need to have a dedicated context for each request, so they do not interfere.

We still can disable this check via allowGlobalContext configuration, or a connected environment variable MIKRO_ORM_ALLOW_GLOBAL_CONTEXT - this can be handy especially in unit tests.

MikroORM.init({
allowGlobalContext: true,
});

Using environment variables

Since v4.5 it is possible to set most of the ORM options via environment variables. By default .env file from the root directory is loaded - it is also possible to set full path to the env file you want to use via MIKRO_ORM_ENV environment variable.

Environment variables always have precedence.

Example .env file:

MIKRO_ORM_TYPE = sqlite
MIKRO_ORM_ENTITIES = ./dist/foo/*.entity.js, ./dist/bar/*.entity.js
MIKRO_ORM_ENTITIES_TS = ./src/foo/*.entity.ts, ./src/bar/*.entity.ts
MIKRO_ORM_DB_NAME = test.db
MIKRO_ORM_MIGRATIONS_PATH = ./dist/migrations
MIKRO_ORM_MIGRATIONS_PATH_TS = ./src/migrations
MIKRO_ORM_POPULATE_AFTER_FLUSH = true
MIKRO_ORM_FORCE_ENTITY_CONSTRUCTOR = true
MIKRO_ORM_FORCE_UNDEFINED = true

Full list of supported options:

env variableconfig key
MIKRO_ORM_BASE_DIRbaseDir
MIKRO_ORM_TYPEtype
MIKRO_ORM_ENTITIESentities
MIKRO_ORM_ENTITIES_TSentitiesTs
MIKRO_ORM_CLIENT_URLclientUrl
MIKRO_ORM_HOSThost
MIKRO_ORM_PORTport
MIKRO_ORM_USERuser
MIKRO_ORM_PASSWORDpassword
MIKRO_ORM_DB_NAMEdbName
MIKRO_ORM_LOAD_STRATEGYloadStrategy
MIKRO_ORM_BATCH_SIZEbatchSize
MIKRO_ORM_USE_BATCH_INSERTSuseBatchInserts
MIKRO_ORM_USE_BATCH_UPDATESuseBatchUpdates
MIKRO_ORM_STRICTstrict
MIKRO_ORM_VALIDATEvalidate
MIKRO_ORM_AUTO_JOIN_ONE_TO_ONE_OWNERautoJoinOneToOneOwner
MIKRO_ORM_PROPAGATE_TO_ONE_OWNERpropagateToOneOwner
MIKRO_ORM_POPULATE_AFTER_FLUSHpopulateAfterFlush
MIKRO_ORM_FORCE_ENTITY_CONSTRUCTORforceEntityConstructor
MIKRO_ORM_FORCE_UNDEFINEDforceUndefined
MIKRO_ORM_FORCE_UTC_TIMEZONEforceUtcTimezone
MIKRO_ORM_TIMEZONEtimezone
MIKRO_ORM_ENSURE_INDEXESensureIndexes
MIKRO_ORM_IMPLICIT_TRANSACTIONSimplicitTransactions
MIKRO_ORM_DEBUGdebug
MIKRO_ORM_VERBOSEverbose
MIKRO_ORM_DISCOVERY_WARN_WHEN_NO_ENTITIESdiscovery.warnWhenNoEntities
MIKRO_ORM_DISCOVERY_REQUIRE_ENTITIES_ARRAYdiscovery.requireEntitiesArray
MIKRO_ORM_DISCOVERY_ALWAYS_ANALYSE_PROPERTIESdiscovery.alwaysAnalyseProperties
MIKRO_ORM_DISCOVERY_DISABLE_DYNAMIC_FILE_ACCESSdiscovery.disableDynamicFileAccess
MIKRO_ORM_MIGRATIONS_TABLE_NAMEmigrations.tableName
MIKRO_ORM_MIGRATIONS_PATHmigrations.path
MIKRO_ORM_MIGRATIONS_PATH_TSmigrations.pathTs
MIKRO_ORM_MIGRATIONS_GLOBmigrations.glob
MIKRO_ORM_MIGRATIONS_TRANSACTIONALmigrations.transactional
MIKRO_ORM_MIGRATIONS_DISABLE_FOREIGN_KEYSmigrations.disableForeignKeys
MIKRO_ORM_MIGRATIONS_ALL_OR_NOTHINGmigrations.allOrNothing
MIKRO_ORM_MIGRATIONS_DROP_TABLESmigrations.dropTables
MIKRO_ORM_MIGRATIONS_SAFEmigrations.safe
MIKRO_ORM_MIGRATIONS_EMITmigrations.emit
MIKRO_ORM_SCHEMA_GENERATOR_DISABLE_FOREIGN_KEYSmigrations.disableForeignKeys
MIKRO_ORM_SCHEMA_GENERATOR_CREATE_FOREIGN_KEY_CONSTRAINTSmigrations.createForeignKeyConstraints
MIKRO_ORM_SEEDER_PATHseeder.path
MIKRO_ORM_SEEDER_PATH_TSseeder.pathTs
MIKRO_ORM_SEEDER_GLOBseeder.glob
MIKRO_ORM_SEEDER_EMITseeder.emit
MIKRO_ORM_SEEDER_DEFAULT_SEEDERseeder.defaultSeeder