Skip to main content
Version: 7.0 (next)

Modeling Entity Relationships

There are 4 types of entity relationships in MikroORM:

  • ManyToOne
  • OneToMany
  • OneToOne
  • ManyToMany

Relations can be unidirectional and bidirectional. Unidirectional are defined only on one side (the owning side). Bidirectional are defined on both sides, while one is owning side (where references are store), marked by inversedBy attribute pointing to the inverse side. On the inversed side we define it with mappedBy attribute pointing back to the owner:

When modeling bidirectional relationship, you can also omit the inversedBy attribute, defining mappedBy on the inverse side is enough as it will be auto-wired.

ManyToOne

Many instances of the current Entity refer to One instance of the referred Entity.

There are multiple ways how to define the relationship, all of the following is equivalent:

import { defineEntity, InferEntity, p } from '@mikro-orm/core';

export const Book = defineEntity({
name: 'Book',
properties: {
id: p.integer().primary(),
author: () => p.manyToOne(Author),
},
});

export interface IBook extends InferEntity<typeof Book> {}

You can also specify how operations on given entity should cascade to the referred entity.

OneToMany

One instance of the current Entity has Many instances (references) to the referred Entity.

Again, all of the following is equivalent:

import { defineEntity, InferEntity, p } from '@mikro-orm/core';

export const Author = defineEntity({
name: 'Author',
properties: {
id: p.integer().primary(),
books: () => p.oneToMany(Book).mappedBy('author'),
},
});

export interface IAuthor extends InferEntity<typeof Author> {}

As you can see, OneToMany is the inverse side of ManyToOne (which is the owning side). More about how collections work can be found on collections page.

You can also specify how operations on given entity should cascade to the referred entities. There is also more aggressive remove mode called Orphan Removal (books4 example).

OneToOne

One instance of the current Entity refers to One instance of the referred Entity.

This is a variant of ManyToOne, where there is always just one entity on both sides. This means that the foreign key column is also unique.

Owning Side

import { defineEntity, InferEntity, p } from '@mikro-orm/core';

export const User = defineEntity({
name: 'User',
properties: {
id: p.integer().primary(),
// when none of `owner/inversedBy/mappedBy` is provided, it will be considered owning side
bestFriend1: () => p.oneToOne(User),
// side with `inversedBy` is the owning one, to define inverse side use `mappedBy`
bestFriend2: () => p.oneToOne(User).inversedBy('bestFriend1'),
// you need to specifically mark the owning side with `owner: true`
bestFriend3: () => p.oneToOne(User).owner(),
},
});

export interface IUser extends InferEntity<typeof User> {}

Inverse Side

import { defineEntity, InferEntity, p } from '@mikro-orm/core';

export const User = defineEntity({
name: 'User',
properties: {
id: p.integer().primary(),
bestFriend1: () => p.oneToOne(User).mappedBy('bestFriend1').orphanRemoval(),
bestFriend2: () => p.oneToOne(User).mappedBy('bestFriend2').orphanRemoval(),
},
});

export interface IUser extends InferEntity<typeof User> {}

As you can see, relationships can be also self-referencing (all of them. OneToOne also supports Orphan Removal).

ManyToMany

Many instances of the current Entity refers to Many instances of the referred Entity.

Here are examples of how you can define ManyToMany relationship:

Owning Side

import { defineEntity, InferEntity, p } from '@mikro-orm/core';

export const Book = defineEntity({
name: 'Book',
properties: {
id: p.integer().primary(),
// when none of `owner/inversedBy/mappedBy` is provided, it will be considered owning side
tags: () => p.manyToMany(BookTag),
// to define uni-directional many to many, simply omit `mappedBy`
friends: () => p.manyToMany(Author),
},
});

export interface IBook extends InferEntity<typeof Book> {}

Inverse Side

import { defineEntity, InferEntity, p } from '@mikro-orm/core';

export const BookTag = defineEntity({
name: 'BookTag',
properties: {
id: p.integer().primary(),
// inverse side has to point to the owning side via `mappedBy`
books: () => p.manyToMany(Book).mappedBy('tags'),
},
});

export interface IBookTag extends InferEntity<typeof BookTag> {}

Again, more information about how collections work can be found on collections page.

Relations in ESM projects

If you use ESM in your TypeScript project with reflect-metadata, you might fall into issues with circular dependencies, seeing errors like this:

ReferenceError: Cannot access 'Author' before initialization

To get around them, use the Rel mapped type. It is an identity type, which disables the problematic inference from reflect-metadata, that causes ESM projects to fail.

import { defineEntity, InferEntity, p } from '@mikro-orm/core';

export const Book = defineEntity({
name: 'Book',
properties: {
id: p.integer().primary(),
author: () => p.manyToOne(Author),
},
});

export interface IBook extends InferEntity<typeof Book> {}

With defineEntity, circular dependencies are handled automatically, no need for Rel wrapper.

Custom foreign key constraint name

If you need a greater control on the underlying SQL schema, you can provide a custom name for the foreign key constraint of your relationship on the owning side.

This name overrides the one automatically generated by the current NamingStrategy.

import { defineEntity, InferEntity, p } from '@mikro-orm/core';

export const Book = defineEntity({
name: 'Book',
properties: {
id: p.integer().primary(),
author: () => p.manyToOne(Author).foreignKeyName('my_custom_name'),
},
});

export interface IBook extends InferEntity<typeof Book> {}

Disabling foreign key constraint creation

If you need to disable the creation of the underlying SQL foreign key constraint for a specific relation, you can set createForeignKeyConstraint to false on the relation on the owning side.

import { defineEntity, InferEntity, p } from '@mikro-orm/core';

export const Book = defineEntity({
name: 'Book',
properties: {
id: p.integer().primary(),
author: () => p.manyToOne(Author).createForeignKeyConstraint(false),
},
});

export interface IBook extends InferEntity<typeof Book> {}

Note that if you globally disable the creation of all foreign key constraints by setting createForeignKeyConstraints to false, then no foreign key constraint is created whatsoever on any relation.

const orm = await MikroORM.init({
...
schemaGenerator: {
createForeignKeyConstraints: false,
},
});