Skip to main content
Version: 7.0 (next)

Using Decorators

This guide covers using class decorators to define entities. While the Getting Started guide uses the defineEntity helper for its simplicity and full type inference, decorators remain a powerful and popular approach for entity definition.

Decorator Types

MikroORM v7 supports two types of decorators:

FeatureLegacy (Experimental) DecoratorsES Spec Decorators
TypeScript configexperimentalDecorators: trueNo special config needed
Package@mikro-orm/decorators/legacy@mikro-orm/decorators/es
Metadata reflectionSupported (with reflect-metadata)Not supported
ts-morph supportYesYes
Transpiler supporttsc, swc, babel (with plugins)tsc, esbuild, swc

Legacy (Experimental) Decorators

Legacy decorators are the traditional TypeScript decorators that have been available for years. They require the experimentalDecorators compiler option.

tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true // only needed with reflect-metadata
}
}
./entities/User.ts
import { Entity, PrimaryKey, Property } from '@mikro-orm/decorators/legacy';

@Entity()
export class User {

@PrimaryKey()
id!: number;

@Property()
fullName!: string;

@Property()
email!: string;

@Property({ nullable: true })
age?: number;

}

ES Spec Decorators (Stage 3)

ES spec decorators follow the TC39 Stage 3 proposal and are now supported natively by TypeScript 5.0+ without any configuration. They work with modern bundlers like esbuild out of the box.

./entities/User.ts
import { Entity, PrimaryKey, Property } from '@mikro-orm/decorators/es';

@Entity()
export class User {

@PrimaryKey()
id!: number;

@Property()
fullName!: string;

@Property()
email!: string;

@Property()
age?: number;

}
ES Spec Decorator Limitations

ES spec decorators do not support metadata reflection. This means:

  • You cannot use reflect-metadata with ES spec decorators
  • You must always provide the target entity type explicitly in relation decorators
  • The TsMorphMetadataProvider can still infer types from your source code

Metadata Providers

When using decorators, MikroORM needs a way to understand the types of your entity properties. This is handled by metadata providers.

ReflectMetadataProvider

The ReflectMetadataProvider uses the reflect-metadata package to read type information emitted by the TypeScript compiler. This approach is fast and lightweight, but has some limitations.

Installation:

npm install reflect-metadata

Configuration:

mikro-orm.config.ts
import { ReflectMetadataProvider } from '@mikro-orm/decorators/legacy';
import { defineConfig } from '@mikro-orm/sqlite';

export default defineConfig({
metadataProvider: ReflectMetadataProvider,
entities: [User, Article], // explicit entity references recommended
// ...
});

TypeScript Configuration:

tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}

Bootstrap:

app.ts
import 'reflect-metadata'; // Must be imported before any entity
import { MikroORM } from '@mikro-orm/sqlite';

Limitations:

When using reflect-metadata, you need to be more explicit in your decorators:

import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/decorators/legacy';

@Entity()
export class Article {

@PrimaryKey()
id!: number;

@Property()
title!: string;

// Must specify `entity` and `nullable` explicitly
@ManyToOne(() => User, { nullable: true })
author?: User;

// Must specify `entity`, `nullable` and `ref` explicitly
@ManyToOne(() => Publisher, { ref: true, nullable: true })
publisher?: Ref<Publisher>;

// Array types need explicit items
@Property({ type: 'string[]' })
tags: string[] = [];

}

Type inference limitations with reflect-metadata:

Scenariots-morphreflect-metadata
Scalar typesAutomaticAutomatic
Optional propertiesInferred as nullableRequires nullable: true
Relation targetsAutomaticRequires entity: () => Entity
Ref<T> wrapperAutomaticRequires ref: true
Array element typesAutomaticRequires explicit type
EnumsAutomaticRequires items: () => Enum
Union typesSupportedNot supported
ES Spec Decorators

ReflectMetadataProvider only works with legacy decorators. ES spec decorators do not support emitDecoratorMetadata, so you cannot use reflect-metadata with them.

TsMorphMetadataProvider

The TsMorphMetadataProvider uses the TypeScript Compiler API (via ts-morph) to read type information directly from your source files. This is slower than ReflectMetadataProvider, but it supports inferring many options from the TypeScript source code.

Installation:

npm install @mikro-orm/reflection

Configuration:

mikro-orm.config.ts
import { TsMorphMetadataProvider } from '@mikro-orm/reflection';
import { defineConfig } from '@mikro-orm/sqlite';

export default defineConfig({
metadataProvider: TsMorphMetadataProvider,
entities: [User, Article],
// ...
});

TypeScript Configuration:

tsconfig.json
{
"compilerOptions": {
"declaration": true,
"experimentalDecorators": true
}
}

The declaration: true option is required because ts-morph reads .d.ts files when running from compiled JavaScript.

Benefits:

  • Automatically infers property types, including complex types and generics
  • Infers nullability from optional properties (?)
  • Works with both legacy and ES spec decorators
  • Allows DRY entity definitions (less decorator options needed)

Considerations:

  • Slower discovery (mitigated by metadata caching)
  • Requires .d.ts files to be generated
  • Not compatible with some bundlers (e.g., webpack in certain configurations)

Folder-based discovery and ESM:

When using folder-based discovery (glob patterns in entities option) in an ESM project with test runners like Vitest, you may encounter an error like TypeError: Unknown file extension ".ts" (ERR_UNKNOWN_FILE_EXTENSION). This happens because the dynamic import of your entities fails to resolve TypeScript files - MikroORM performs these imports internally, and tools like Vitest cannot automatically transform them.

To work around this, you can override the dynamicImportProvider option in your ORM config. This allows you to use an import call defined inside the context of your ESM application:

mikro-orm.config.ts
export default defineConfig({
// ...
// for vitest to get around `TypeError: Unknown file extension ".ts"` (ERR_UNKNOWN_FILE_EXTENSION)
dynamicImportProvider: id => import(id),
});

This tells MikroORM to use your application's import context instead of its own, allowing proper TypeScript file resolution.

Example - DRY entity definition with ts-morph:

import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/decorators/legacy';

@Entity()
export class Article {

@PrimaryKey()
id!: number;

@Property()
title!: string;

// ts-morph infers: type is User, nullable is true
@ManyToOne()
author?: User;

// ts-morph infers the Ref wrapper and target entity
@ManyToOne()
publisher?: Ref<Publisher>;

}

Metadata Caching

When using TsMorphMetadataProvider, the discovery process can be slow due to TypeScript parsing. MikroORM caches the metadata to speed up subsequent startups.

mikro-orm.config.ts
import { defineConfig } from '@mikro-orm/sqlite';
import { TsMorphMetadataProvider } from '@mikro-orm/reflection';

export default defineConfig({
metadataProvider: TsMorphMetadataProvider,
metadataCache: {
enabled: true, // enabled by default for TsMorphMetadataProvider
// Cache is stored in `temp` folder by default
// Add this folder to .gitignore
},
});

For production deployments, you can generate the cache at build time:

npx mikro-orm cache:generate

This creates a production-ready cache that eliminates the need for ts-morph at runtime.

See Metadata Cache for more details.

Comparison of Approaches

ApproachProsCons
defineEntityFull type inference, no decorators, works everywhereDifferent syntax than traditional ORMs
ES decorators + ts-morphModern standard, DRY definitionsSlower startup, requires .d.ts files
Legacy decorators + ts-morphDRY definitions, familiar syntaxSlower startup, requires config
Legacy decorators + reflect-metadataFast startup, lightweightVerbose, limited type inference

Migration from v6

In MikroORM v7, decorators were moved to a separate package:

- import { Entity, PrimaryKey, Property } from '@mikro-orm/core';
+ import { Entity, PrimaryKey, Property } from '@mikro-orm/decorators/legacy';

For ES spec decorators:

import { Entity, PrimaryKey, Property } from '@mikro-orm/decorators/es';

The ReflectMetadataProvider is also moved:

- import { ReflectMetadataProvider } from '@mikro-orm/core';
+ import { ReflectMetadataProvider } from '@mikro-orm/decorators/legacy';

See Upgrading from v6 to v7 for the complete migration guide.