Skip to content

MikroORM

Databases & CachingDatabase/ORMTypeScript

What it is

MikroORM is a TypeScript ORM built on the Data Mapper, Unit of Work and Identity Map patterns, tracking entity changes and flushing them in one transaction.

The EntityManager tracks loaded entities. Mutating a managed entity records the change; flush() writes everything in a single transaction.

Installation

npm install @mikro-orm/core @mikro-orm/postgresql

Getting started

The smallest useful thing you can do with it, and what each part means.

Unit of Work in practice
@Entity()
export class Book {
  @PrimaryKey() id!: number;
  @Property() title!: string;
  @ManyToOne(() => Author) author!: Author;
}

const book = await em.findOneOrFail(Book, 42, { populate: ['author'] });

book.title = 'Dune Messiah';        // no query yet — just tracked
book.author.name = 'F. Herbert';    // also tracked

await em.flush();                    // one transaction, minimal UPDATEs
There is no save() call. The EntityManager diffs what changed and issues only the necessary statements — the defining behaviour of Unit of Work.

Advanced usage

Where the library earns its place over a simpler alternative.

Request-scoped context
// A shared EntityManager across requests leaks identity-map state
// between users. Fork one per request.
app.use((req, res, next) => {
  RequestContext.create(orm.em, next);
});

// Or explicitly:
const em = orm.em.fork();
const books = await em.find(Book, { year: { $gte: 1990 } }, {
  populate: ['author'],
  orderBy: { year: 'DESC' },
  limit: 10,
});
This is the mistake that matters most with MikroORM: reusing the global EntityManager means one request can see another's cached entities.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Entity is not populated
Access requires loading. Add it to populate, or call wrap(entity).init().
Data from another request appears
The global EntityManager was reused. Fork per request.

Best practices

  • Always fork the EntityManager per request, or use RequestContext middleware.
  • Populate relations explicitly; unpopulated ones are references, not loaded objects.
  • Call flush once per unit of work rather than after each mutation.
  • Use migrations rather than schema generation in production.

Background

Why it exists, and what it was reacting to.

Created by Martin Adámek, MikroORM brought Doctrine's and Hibernate's Unit of Work model to TypeScript — you mutate entities and the ORM computes the minimal set of queries at flush time.