Metadata Providers
As part of entity discovery process, MikroORM uses so called MetadataProvider
to get necessary type information about our entities' properties.
We can also implement custom metadata provider by extending abstract
MetadataProvider
class.
There are 3 built-in metadata providers we can use:
TsMorphMetadataProvider
With TsMorphMetadataProvider
MikroORM will use ts-morph
to read TypeScript source files of all entities to be able to detect all types. Thanks to this, defining the type is enough for runtime validation.
To use it, first install the @mikro-orm/reflection
package.
import { TsMorphMetadataProvider } from '@mikro-orm/reflection';
await MikroORM.init({
metadataProvider: TsMorphMetadataProvider,
// ...
});
If we use folder-based discovery, we should specify paths to the compiled entities via entities
as well as paths to the TS source files of those entities via entitiesTs
. When we run the ORM via ts-node
, the latter will be used automatically, or if we explicitly pass tsNode: true
in the config. Note that tsNode: true
should not be part of production config.
When running via
node
,.d.ts
files are used to obtain the type, so we need to ship them in the production build. TS source files are no longer needed (since v4). Be sure to enablecompilerOptions.declaration
in ourtsconfig.json
.
After the discovery process ends, all metadata will be cached. By default, FileCacheAdapter
will be used to store the cache inside ./temp
folder in JSON files.
We can generate production cache via CLI command
mikro-orm cache:generate
.
We can implement custom cache adapter by implementing
CacheAdapter
interface.
ReflectMetadataProvider
ReflectMetadataProvider
uses reflect-metadata
module to read the type from decorator metadata exported by TypeScript compiler.
We will need to install reflect-metadata
module and import at the top of our app's bootstrap script (e.g. main.ts
or app.ts
).
import 'reflect-metadata';
Next step is to enable emitDecoratorMetadata
flag in our tsconfig.json
.
As this approach does not have performance impact, metadata caching is not really necessary.
await MikroORM.init({
metadataProvider: ReflectMetadataProvider,
// ...
});
Limitations and requirements
While TsMorphMetadataProvider
do not suffer from any of the following problems, it brings a performance penalty (that can be limited only to cases where you change your entity definition via metadata caching) and is in general not compatible with other compilers like webpack or babel. It also requires you to deploy .d.ts
files alongside your compiled .js
entities (more about this in the deployment section.
Explicit types
Since v6, the type is inferred automatically based on the runtime default value, as long as it is possible to instantiate the entity without passing any constructor arguments. As long as you keep your constructors aware of that, this limitation does not apply.
Type inference is not supported, we need to always explicitly specify the type:
@Property()
createdAt: Date = new Date();
Collection properties and Identified references
We need to provide target entity type in @OneToMany
and @ManyToMany
decorators:
@OneToMany(() => Book, b => b.author)
books = new Collection<Book>(this);
@ManyToOne(() => Publisher, { ref: true })
publisher!: Ref<Publisher>;
Optional properties
Reading property nullability is not supported, we need to explicitly set nullable
attribute:
@Property({ nullable: true })
prop?: string;
Enums
By default, enum is considered as numeric type. For string enums, we need to explicitly provide one of:
- reference to the enum (which will force us to define the enum before defining the entity)
@Enum(() => UserRole)
role: UserRole; - name of the enum (if it is present in the same file)
@Enum({ type: 'UserRole' })
role: UserRole; - list of the enum items
@Enum({ items: ['a', 'b', 'c'] })
role: UserRole;
Circular dependencies
Reading type of referenced entity in @ManyToOne
and @OneToOne
properties fails if there is circular dependency. We will need to explicitly define the type in the decorator (preferably via entity: () => ...
callback).
@ManyToOne({ entity: () => Author })
author: Author;
There can be recursion issues when we define multiple entities (with circular dependencies between each other) in single file. In that case, we might want to provide the type via decorator's
type
orentity
attributes and set the TS property type to something else (likeany
orobject
).
Additional typings might be required
We might have to install additional typings, one example is use of ObjectId
in MongoDB, which requires @types/mongodb
to be installed.
Using EntitySchema
Alternatively, you can use EntitySchema
instead, which is suitable for JavaScript only projects.
If you want to have absolute control, you can also use
EntitySchema.fromMetadata()
factory method.
Read more about it in Usage with JavaScript section.