The wrap() Helper
The wrap() helper provides access to useful methods for working with entities. It returns a WrappedEntity instance that exposes methods for checking entity state, loading relations, serializing, and updating values.
import { wrap } from '@mikro-orm/core';
const book = await em.findOne(Book, 1);
// Check if the entity is initialized
wrap(book).isInitialized();
// Load a relation
await wrap(book.author).init();
// Update entity values
wrap(book).assign({ title: 'New Title' });
// Serialize to plain object
const dto = wrap(book).toObject();
wrap() vs BaseEntity
There are two ways to access these helper methods:
1. Using wrap() helper - keeps your entity class clean:
- defineEntity
- reflect-metadata
- ts-morph
- EntitySchema
import { defineEntity, p } from '@mikro-orm/core';
export const Book = defineEntity({
name: 'Book',
properties: {
id: p.integer().primary(),
title: p.string(),
},
});
// Access helpers via wrap()
wrap(book).assign({ title: 'New Title' });
import { Entity, PrimaryKey, Property } from '@mikro-orm/core';
@Entity()
export class Book {
@PrimaryKey()
id!: number;
@Property()
title!: string;
}
// Access helpers via wrap()
wrap(book).assign({ title: 'New Title' });
import { Entity, PrimaryKey, Property } from '@mikro-orm/core';
@Entity()
export class Book {
@PrimaryKey()
id!: number;
@Property()
title!: string;
}
// Access helpers via wrap()
wrap(book).assign({ title: 'New Title' });
import { EntitySchema } from '@mikro-orm/core';
export interface IBook {
id: number;
title: string;
}
export const Book = new EntitySchema<IBook>({
name: 'Book',
properties: {
id: { type: Number, primary: true },
title: { type: String },
},
});
// Access helpers via wrap()
wrap(book).assign({ title: 'New Title' });
2. Extending BaseEntity - methods available directly on the entity:
- defineEntity
- reflect-metadata
- ts-morph
- EntitySchema
import { defineEntity, p, BaseEntity } from '@mikro-orm/core';
export const Book = defineEntity({
name: 'Book',
extends: BaseEntity,
properties: {
id: p.integer().primary(),
title: p.string(),
},
});
// Access helpers directly
book.assign({ title: 'New Title' });
import { BaseEntity, Entity, PrimaryKey, Property } from '@mikro-orm/core';
@Entity()
export class Book extends BaseEntity {
@PrimaryKey()
id!: number;
@Property()
title!: string;
}
// Access helpers directly
book.assign({ title: 'New Title' });
import { BaseEntity, Entity, PrimaryKey, Property } from '@mikro-orm/core';
@Entity()
export class Book extends BaseEntity {
@PrimaryKey()
id!: number;
@Property()
title!: string;
}
// Access helpers directly
book.assign({ title: 'New Title' });
import { EntitySchema, BaseEntity } from '@mikro-orm/core';
export interface IBook {
id: number;
title: string;
}
export const Book = new EntitySchema<IBook>({
name: 'Book',
extends: BaseEntity,
properties: {
id: { type: Number, primary: true },
title: { type: String },
},
});
// Access helpers directly
book.assign({ title: 'New Title' });
Choose based on whether you want to keep your entity interface clean (wrap()) or prefer the convenience of direct method access (BaseEntity).
Available Methods
State Methods
| Method | Description |
|---|---|
isInitialized() | Returns true if the entity is fully loaded (not just a reference) |
isManaged() | Returns true if the entity is tracked by an EntityManager |
populated(flag?) | Marks the entity as populated (or not) |
const author = em.getReference(Author, 1);
wrap(author).isInitialized(); // false - just a reference
await wrap(author).init();
wrap(author).isInitialized(); // true - now loaded
Loading Methods
| Method | Description |
|---|---|
init(populated?, populate?, lockMode?) | Loads the entity from the database |
populate(populate, options?) | Loads specified relations |
// Load an uninitialized reference
const author = em.getReference(Author, 1);
await wrap(author).init();
// Load specific relations
const book = await em.findOne(Book, 1);
await wrap(book).populate(['author', 'tags']);
Serialization Methods
| Method | Description |
|---|---|
toObject(ignoreFields?) | Converts entity to a plain object |
toJSON() | Same as toObject(), used by JSON.stringify() |
toPOJO() | Same as toObject() |
serialize(options?) | Converts to plain object with more control |
setSerializationContext(options?) | Sets the serialization context for subsequent serialization |
See Serializing for detailed serialization options.
const book = await em.findOne(Book, 1, { populate: ['author'] });
// Basic serialization
const dto = wrap(book).toObject();
// With options
const dto2 = wrap(book).serialize({
populate: ['author'],
exclude: ['createdAt'],
});
Reference Methods
| Method | Description |
|---|---|
toReference() | Wraps the entity in a Reference wrapper |
const author = await em.findOne(Author, 1);
book.author = wrap(author).toReference();
Schema Methods
| Method | Description |
|---|---|
getSchema() | Returns the entity's schema name |
setSchema(schema?) | Sets the entity's schema name |
Accessing Internal Properties
To access internal properties like __meta or __em, pass true as the second argument:
// Public API only
wrap(author);
// Include internal properties
wrap(author, true).__meta;
wrap(author, true).__em;
Updating Entities with assign()
The assign() method is one of the most commonly used helpers. It updates entity values from plain objects, automatically handling relations by converting IDs to references.
Basic Usage
Without assign(), updating relations requires manual reference creation:
const book = await em.findOne(Book, 1);
book.title = 'New Title';
book.author = em.getReference(Author, authorId);
With assign(), you can pass plain data including relation IDs:
wrap(book).assign({
title: 'New Title',
author: authorId, // automatically converted to reference
});
This is particularly useful when handling user input or API payloads:
// Express route handler
app.patch('/books/:id', async (req, res) => {
const book = await em.findOneOrFail(Book, req.params.id);
wrap(book).assign(req.body);
await em.flush();
res.json(book);
});
Assigning to Unmanaged Entities
For entities not yet managed by an EntityManager, pass em in the options:
const book = new Book();
wrap(book).assign({
title: 'New Book',
author: authorId,
}, { em });
Merging Object Properties
By default, assign() replaces object properties (like Object.assign()). Enable deep merging with mergeObjectProperties:
book.meta = { foo: 1, bar: 2 };
// Without merging - replaces the object
wrap(book).assign({ meta: { foo: 3 } });
console.log(book.meta); // { foo: 3 }
// With merging - deep merges
wrap(book).assign({ meta: { foo: 3 } }, { mergeObjectProperties: true });
console.log(book.meta); // { foo: 3, bar: 2 }
Embedded properties are merged by default. Disable with mergeEmbeddedProperties: false.
Updating Nested Relations
To update a nested entity, include its primary key in the data and ensure it's loaded in the current context:
const book = await em.findOneOrFail(Book, 1, { populate: ['author'] });
// Update the existing author
wrap(book).assign({
author: {
id: book.author.id,
name: 'Updated Name',
},
});
Without the primary key, the nested object is treated as a new entity (triggers INSERT):
// Creates a NEW author
wrap(book).assign({
author: {
name: 'New Author',
},
});
Use updateByPrimaryKey: false to update existing relations without providing the PK:
wrap(book).assign({
author: {
name: 'Updated Name',
},
}, { updateByPrimaryKey: false });
Updating Collections
Pass an array to replace collection items, or a single item to append:
// Replace all addresses
wrap(user).assign({ addresses: [new Address(...), new Address(...)] });
// Append to existing addresses
wrap(user).assign({ addresses: new Address(...) });
Using Class-Based DTOs
When using validation libraries like class-validator, extend PlainObject to let MikroORM treat your DTO as a plain object:
import { PlainObject } from '@mikro-orm/core';
class UpdateBookDTO extends PlainObject {
@IsString()
title!: string;
@IsNumber()
authorId!: number;
}
// dto is an instance of UpdateBookDTO
wrap(book).assign(dto);
assign() Options
| Option | Default | Description |
|---|---|---|
updateNestedEntities | true | Process nested relation objects |
updateByPrimaryKey | true | Require PK to update existing relations |
mergeObjectProperties | false | Deep merge JSON/object properties |
mergeEmbeddedProperties | true | Deep merge embedded properties |
onlyProperties | false | Ignore unknown properties in payload |
onlyOwnProperties | false | Skip inverse relations, convert others to FKs |
convertCustomTypes | false | Allow raw database values for custom types |
ignoreUndefined | false | Skip undefined properties |
merge | false | Use em.merge() instead of em.create() for new relations |
schema | (inferred) | Schema for looking up relation entities |
em | (inferred) | EntityManager instance (required for unmanaged entities) |
Global Configuration
Configure default assign() behavior in your ORM config:
await MikroORM.init({
assign: {
updateNestedEntities: true,
updateByPrimaryKey: true,
mergeObjectProperties: false,
mergeEmbeddedProperties: true,
},
});