# Source: https://mikro-orm.io/docs/defining-entities.md

# Defining Entities

Entities are simple javascript objects (so called POJO) without restrictions and without the need to extend base classes. Using [entity constructors](https://mikro-orm.io/docs/entity-constructors.md) works as well - they are never executed for managed entities (loaded from database). Every entity is required to have a primary key.

Entities can be defined in three ways:

* Decorated classes - the attributes of the entity, as well as each property are provided via decorators. We use `@Entity()` decorator on the class. Entity properties are decorated either with `@Property` decorator, or with one of reference decorators: `@ManyToOne`, `@OneToMany`, `@OneToOne` and `@ManyToMany`. Check out the full [decorator reference](https://mikro-orm.io/docs/decorators.md).
* `EntitySchema` helper - With `EntitySchema` helper we define the schema programmatically. We can use regular classes as well as interfaces. This approach also allows to re-use partial entity definitions (e.g. traits/mixins). Read more about this in [Defining Entities via EntitySchema section](https://mikro-orm.io/docs/entity-schema.md).
* `defineEntity` helper - Based on the `EntitySchema`, automatically infers entity interfaces using TypeScript's inference capabilities. Read more about this in [`defineEntity` section](https://mikro-orm.io/docs/entity-schema.md#defineentity).

Moreover, how the metadata extraction from decorators happens is controlled via `MetadataProvider`. Two main metadata providers are:

* `ReflectMetadataProvider` - uses `reflect-metadata` to read the property types. Faster but simpler and more verbose.
* `TsMorphMetadataProvider` - uses `ts-morph` to read the type information from the TypeScript compiled API. Heavier (requires full TS as a dependency), but allows DRY entity definition. With `ts-morph` we are able to extract the type as it is defined in the code, including interface names, as well as optionality of properties.

Read more about them in the [Metadata Providers section](https://mikro-orm.io/docs/metadata-providers.md).

> Current set of decorators in MikroORM is designed to work with the `tsc`. Using `babel` and `swc` is also possible, but requires some additional setup. Read more about it [here](https://mikro-orm.io/docs/usage-with-transpilers.md). For notes about `webpack`, read the [deployment section](https://mikro-orm.io/docs/deployment.md).
>
> `ts-morph` is compatible only with the `tsc` approach.

> From v3 we can also use default exports when defining your entity.

Example definition of a `Book` entity follows. We can switch the tabs to see the difference for various ways:

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Book.ts

```
@Entity()
export class Book extends CustomBaseEntity {

  @Property()
  title!: string;

  @ManyToOne(() => Author)
  author!: Author;

  @ManyToOne(() => Publisher, { ref: true, nullable: true })
  publisher?: Ref<Publisher>;

  @ManyToMany({ entity: 'BookTag', fixedOrder: true })
  tags = new Collection<BookTag>(this);

}
```

./entities/Book.ts

```
@Entity()
export class Book extends CustomBaseEntity {

  @Property()
  title!: string;

  @ManyToOne()
  author!: Author;

  @ManyToOne()
  publisher?: Ref<Publisher>;

  @ManyToMany({ fixedOrder: true })
  tags = new Collection<BookTag>(this);

}
```

./entities/Book.ts

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

export const Book = defineEntity({
  name: 'Book',
  properties: p => ({
    ...CustomBaseProperties,
    title: p.string(),
    author: () => p.manyToOne(Author),
    publisher: () => p.manyToOne(Publisher)
      .ref()
      .nullable(),
    tags: () => p.manyToMany(BookTag)
      .fixedOrder(),
  }),
});

export interface IBook extends InferEntity<typeof Book> {}
```

./entities/Book.ts

```
export interface IBook extends CustomBaseEntity {
  title: string;
  author: Author;
  publisher?: Ref<Publisher>;
  tags: Collection<BookTag>;
}

export const Book = new EntitySchema<IBook, CustomBaseEntity>({
  name: 'Book',
  extends: 'CustomBaseEntity',
  properties: {
    title: { type: 'string' },
    author: { kind: 'm:1', entity: 'Author' },
    publisher: { kind: 'm:1', entity: 'Publisher', ref: true, nullable: true },
    tags: { kind: 'm:n', entity: 'BookTag', fixedOrder: true },
  },
});
```

> Including `{ ref: true }` in your `Ref` property definitions will wrap the reference, providing access to helper methods like `.load` and `.unwrap`, which can be helpful for loading data and changing the type of your references where you plan to use them.

Here is another example of `Author` entity, that was referenced from the `Book` one, this time defined for mongo:

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Author.ts

```
@Entity()
export class Author {

  @PrimaryKey()
  _id!: ObjectId;

  @SerializedPrimaryKey()
  id!: string;

  @Property()
  createdAt = new Date();

  @Property({ onUpdate: () => new Date() })
  updatedAt = new Date();

  @Property()
  name!: string;

  @Property()
  email!: string;

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

  @Property()
  termsAccepted: boolean = false;

  @Property({ nullable: true })
  identities?: string[];

  @Property({ nullable: true })
  born?: string;

  @OneToMany(() => Book, book => book.author)
  books = new Collection<Book>(this);

  @ManyToMany(() => Author)
  friends = new Collection<Author>(this);

  @ManyToOne(() => Book, { nullable: true })
  favouriteBook?: Book;

  @Property({ version: true })
  version!: number;

  constructor(name: string, email: string) {
    this.name = name;
    this.email = email;
  }

}
```

./entities/Author.ts

```
@Entity()
export class Author {

  @PrimaryKey()
  _id!: ObjectId;

  @SerializedPrimaryKey()
  id!: string;

  @Property()
  createdAt = new Date();

  @Property({ onUpdate: () => new Date() })
  updatedAt = new Date();

  @Property()
  name!: string;

  @Property()
  email!: string;

  @Property()
  age?: number;

  @Property()
  termsAccepted = false;

  @Property()
  identities?: string[];

  @Property()
  born?: string;

  @OneToMany(() => Book, book => book.author)
  books = new Collection<Book>(this);

  @ManyToMany()
  friends = new Collection<Author>(this);

  @ManyToOne()
  favouriteBook?: Book;

  @Property({ version: true })
  version!: number;

  constructor(name: string, email: string) {
    this.name = name;
    this.email = email;
  }

}
```

./entities/Author.ts

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

export const Author = defineEntity({
  name: 'Author',
  properties: p => ({
    _id: p.type(ObjectId).primary(),
    id: p.string().serializedPrimaryKey(),
    createdAt: p.datetime().onCreate(() => new Date()),
    updatedAt: p.datetime()
      .onCreate(() => new Date())
      .onUpdate(() => new Date()),
    name: p.string(),
    email: p.string(),
    age: p.integer().nullable(),
    termsAccepted: p.boolean(),
    identities: p.array().nullable(),
    born: p.date().nullable(),
    books: () => p.oneToMany(Book).mappedBy(book => book.author),
    friends: () => p.manyToMany(Author),
    favouriteBook: () => p.manyToOne(Book).nullable(),
    version: p.integer().version(),
  }),
});

export interface IAuthor extends InferEntity<typeof Author> {}
```

./entities/Author.ts

```
export class Author {

  _id!: ObjectId;
  id!: string;
  createdAt = new Date();
  updatedAt = new Date();
  name!: string;
  email!: string;
  age?: number;
  termsAccepted = false;
  identities?: string[];
  born?: string;
  books = new Collection<Book>(this);
  friends = new Collection<Author>(this);
  favouriteBook?: Book;
  version!: number;

  constructor(name: string, email: string) {
    this.name = name;
    this.email = email;
  }

}

export const AuthorSchema = new EntitySchema({
  class: Author,
  properties: {
    _id: { type: 'ObjectId', primary: true },
    id: { type: String, serializedPrimaryKey: true },
    createdAt: { type: Date },
    updatedAt: { type: Date, onUpdate: () => new Date() },
    name: { type: String },
    email: { type: String },
    age: { type: Number, nullable: true },
    termsAccepted: { type: Boolean },
    identities: { type: 'string[]', nullable: true },
    born: { type: 'date', nullable: true },
    books: { kind: '1:m', entity: () => Book, mappedBy: book => book.author },
    friends: { kind: 'm:n', entity: () => Author },
    favouriteBook: { kind: 'm:1', entity: () => Book, nullable: true },
    version: { type: Number, version: true },
  },
});
```

More information about modelling relationships can be found on [modelling relationships page](https://mikro-orm.io/docs/relationships.md).

For an example of Vanilla JavaScript usage, take a look [here](https://mikro-orm.io/docs/usage-with-js.md).

## Optional Properties[​](#optional-properties "Direct link to Optional Properties")

With the default `reflect-metadata` provider, we need to mark each optional property as `nullable: true`. When using `ts-morph`, if you define the property as optional (marked with `?`), this will be automatically considered as nullable property (mainly for SQL schema generator).

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Author.ts

```
@ManyToOne(() => Book, { nullable: true })
favouriteBook?: Book;
```

./entities/Author.ts

```
@ManyToOne()
favouriteBook?: Book;
```

./entities/Author.ts

```
const SomeEntity = defineEntity({
  name: 'SomeEntity',
  properties: p => ({
    favouriteBook: p.manyToOne(Book).nullable(),
  }),
});

export interface ISomeEntity extends InferEntity<typeof SomeEntity> {}
```

./entities/Author.ts

```
properties: {
  favouriteBook: { kind: 'm:1', entity: () => Book, nullable: true },
},
```

To make a nullable field required in methods like `em.create()` (i.e. you cannot omit the property), use `RequiredNullable` type. Such property needs to be provided explicitly in the `em.create()` method, but will accept a `null` value.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Book.ts

```
class Book {
  @Property()
  title!: RequiredNullable<string>;
}

em.create(Book, { title: "Alice in Wonderland" }); // ok
em.create(Book, { title: null }); // ok
em.create(Book, {}); // compile error: missing title
```

./entities/Book.ts

```
class Book {
  @Property()
  title!: RequiredNullable<string>;
}

em.create(Book, { title: "Alice in Wonderland" }); // ok
em.create(Book, { title: null }); // ok
em.create(Book, {}); // compile error: missing title
```

./entities/Book.ts

```
const Book = defineEntity({
  name: 'Book',
  properties: p => ({
    title: p.string().$type<RequiredNullable<string>>(),
  }),
});

em.create(Book, { title: "Alice in Wonderland" }); // ok
em.create(Book, { title: null }); // ok
em.create(Book, {}); // compile error: missing title
```

./entities/Author.ts

```
class Book {
  title!: RequiredNullable<string>;
}

const schema = new EntitySchema({
  class: Book,
  properties: {
    title: { type: 'string' },
  },
});

em.create(Book, { title: "Alice in Wonderland" }); // ok
em.create(Book, { title: null }); // ok
em.create(Book, {}); // compile error: missing title
```

## Default values[​](#default-values "Direct link to Default values")

We can set default value of a property in 2 ways:

1. Use a property initializer. This approach should be preferred as long as we are not using any native database function like `now()`. With this approach our entities will have the default value set even before it is actually persisted into the database (e.g. when we instantiate new entity via `new Author()` or `em.create(Author, { ... })`).

> This is only possible if you have an actual entity class, not an interface. If you use `EntitySchema` without a class, or `defineEntity` helper, you can use the `onCreate` option to set the default value. Note that this is processed during `flush`, so the default value will not be available on the entity instance before it is persisted. Since v6.5, you can use the `processOnCreateHooksEarly` config option, to run those hooks right when `em.create` is called.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Author.ts

```
@Property()
foo: number & Opt = 1;

@Property()
bar: string & Opt = 'abc';

@Property()
baz: Date & Opt = new Date();
```

./entities/Author.ts

```
@Property()
foo: number & Opt = 1;

@Property()
bar: string & Opt = 'abc';

@Property()
baz: Date & Opt = new Date();
```

./entities/Author.ts

```
const SomeEntity = defineEntity({
  name: 'SomeEntity',
  properties: p => ({
    foo: p.number().onCreate(() => 1),
    bar: p.string().onCreate(() => 'abc'),
    baz: p.datetime().onCreate(() => new Date()),
  }),
});
```

./entities/Author.ts

```
class Author {

  foo: number & Opt = 1;
  bar: string & Opt = 'abc';
  baz: Date & Opt = new Date();

}

const schema = new EntitySchema({
  class: Author,
  properties: {
    foo: { type: Number },
    bar: { type: String },
    baz: { type: Date },
  },
});
```

2. Use `default` parameter of `@Property` decorator. This way the actual default value will be provided by the database, and automatically mapped to the entity property after it is being persisted (after flush). To use SQL functions like `now()`, use `defaultRaw`.

> Since v4 you should use `defaultRaw` for SQL functions, as `default` with string values will be automatically quoted.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Author.ts

```
@Property({ default: 1 })
foo!: number & Opt;

@Property({ default: 'abc' })
bar!: string & Opt;

@Property({ defaultRaw: 'now' })
baz!: Date & Opt;
```

./entities/Author.ts

```
@Property({ default: 1 })
foo!: number & Opt;

@Property({ default: 'abc' })
bar!: string & Opt;

@Property({ defaultRaw: 'now' })
baz!: Date & Opt;
```

./entities/Author.ts

```
const SomeEntity = defineEntity({
  name: 'SomeEntity',
  properties: p => ({
    foo: p.number().default(1),
    bar: p.string().default('abc'),
    baz: p.datetime().defaultRaw('now'),
  }),
});
```

./entities/Author.ts

```
properties: {
  foo: { type: Number, default: 1 },
  bar: { type: String, default: 'abc' },
  baz: { type: Date, defaultRaw: 'now' },
},
```

Note that we use the `Opt` type to intersect with the property type to tell the ORM (on type level) that the property should be considered optional for input types (e.g. in `em.create()`), but will be present for managed entities (e.g. `EntityDTO` type).

## Enums[​](#enums "Direct link to Enums")

To define an enum property, use `@Enum()` decorator. Enums can be either numeric or string values.

For schema generator to work properly in case of string enums, we need to define the enum in the same file as where it is used, so its values can be automatically discovered. If we want to define the enum in another file, we should re-export it also in place where we use it.

Another possibility is to provide the reference to the enum implementation in the decorator via `@Enum(() => UserRole)`.

> We can also set enum items manually via `items: string[]` attribute.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/User.ts

```
import { OutsideEnum } from './OutsideEnum.ts';

@Entity()
export class User {

  @Enum(() => UserRole)
  role!: UserRole; // string enum

  @Enum(() => UserStatus)
  status!: UserStatus; // numeric/const enum

  @Enum(() => OutsideEnum)
  outside!: OutsideEnum; // string enum defined outside of this file

  @Enum({ items: () => OutsideNullableEnum, nullable: true })
  outsideNullable?: OutsideNullableEnum; // string enum defined outside of this file, may be null

}

export enum UserRole {
  ADMIN = 'admin',
  MODERATOR = 'moderator',
  USER = 'user',
}

export const enum UserStatus {
  DISABLED,
  ACTIVE,
}

// or we could reexport OutsideEnum
// export { OutsideEnum } from './OutsideEnum.ts';
```

./entities/User.ts

```
import { OutsideEnum } from './OutsideEnum.ts';

@Entity()
export class User {

  @Enum(() => UserRole)
  role!: UserRole; // string enum

  @Enum(() => UserStatus)
  status!: UserStatus; // numeric enum

  @Enum(() => OutsideEnum)
  outside!: OutsideEnum; // string enum defined outside of this file

  @Enum({ items: () => OutsideNullableEnum })
  outsideNullable?: OutsideNullableEnum; // string enum defined outside of this file, may be null

}

export enum UserRole {
  ADMIN = 'admin',
  MODERATOR = 'moderator',
  USER = 'user',
}

export const enum UserStatus {
  DISABLED,
  ACTIVE,
}

// or we could reexport OutsideEnum
// export { OutsideEnum } from './OutsideEnum.ts';
```

./entities/User.ts

```
const SomeEntity = defineEntity({
  name: 'SomeEntity',
  properties: p => ({
    // string enum
    role: p.enum(['admin', 'user']),
    // numeric enum
    status: p.enum(() => UserStatus),
    // string enum defined outside of this file
    outside: p.enum(() => OutsideEnum),
    // string enum defined outside of this file, may be null
    outsideNullable: p.enum(() => OutsideNullableEnum).nullable(),
  }),
});
```

./entities/User.ts

```
properties: {
  // string enum
  role: { enum: true, items: () => UserRole },
  // numeric enum
  status: { enum: true, items: () => UserStatus },
  // string enum defined outside of this file
  outside: { enum: true, items: () => OutsideEnum },
  // string enum defined outside of this file, may be null
  outsideNullable: { enum: true, items: () => OutsideNullableEnum, nullable: true },
},
```

### PostgreSQL native enums[​](#postgresql-native-enums "Direct link to PostgreSQL native enums")

By default, the PostgreSQL driver, represents enums as a text columns with check constraints. Since v6, you can opt in for a native enums by setting the `nativeEnumName` option.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/User.ts

```
@Entity()
export class User {

  @Enum({ items: () => UserRole, nativeEnumName: 'user_role' })
  role!: UserRole;

}

export enum UserRole {
  ADMIN = 'admin',
  MODERATOR = 'moderator',
  USER = 'user',
}
```

./entities/User.ts

```
@Entity()
export class User {

  @Enum({ items: () => UserRole, nativeEnumName: 'user_role' })
  role!: UserRole;

}

export enum UserRole {
  ADMIN = 'admin',
  MODERATOR = 'moderator',
  USER = 'user',
}
```

./entities/User.ts

```
export enum UserRole {
  ADMIN = 'admin',
  MODERATOR = 'moderator',
  USER = 'user',
}

export const SomeEntity = defineEntity({
  name: 'SomeEntity',
  properties: p => ({
    role: p.enum(() => UserRole).nativeEnumName('user_role'),
  }),
});
```

./entities/User.ts

```
export enum UserRole {
  ADMIN = 'admin',
  MODERATOR = 'moderator',
  USER = 'user',
}

properties: {
  role: { enum: true, nativeEnumName: 'user_role', items: () => UserRole },
},
```

## Enum arrays[​](#enum-arrays "Direct link to Enum arrays")

We can also use array of values for enum, in that case, `EnumArrayType` type will be used automatically, that will validate items on flush.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/User.ts

```
enum Role {
  User = 'user',
  Admin = 'admin',
}

@Enum({ items: () => Role, array: true, default: [Role.User] })
roles = [Role.User];
```

./entities/User.ts

```
enum Role {
  User = 'user',
  Admin = 'admin',
}

@Enum({ default: [Role.User] })
roles = [Role.User];
```

./entities/User.ts

```
enum Role {
  User = 'user',
  Admin = 'admin',
}

export const SomeEntity = defineEntity({
  name: 'SomeEntity',
  properties: p => ({
    roles: p.enum(() => Role).array().default([Role.User]),
  }),
});
```

./entities/User.ts

```
enum Role {
  User = 'user',
  Admin = 'admin',
}

properties: {
  roles: { enum: true, array: true, default: [Role.User], items: () => Role },
},
```

## Mapping directly to primary keys[​](#mapping-directly-to-primary-keys "Direct link to Mapping directly to primary keys")

Sometimes we might want to work only with the primary key of a relation. To do that, we can use `mapToPk` option on M:1 and 1:1 relations:

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/User.ts

```
@ManyToOne(() => User, { mapToPk: true })
user: number;
```

./entities/User.ts

```
@ManyToOne(() => User, { mapToPk: true })
user: number;
```

./entities/User.ts

```
export const SomeEntity = defineEntity({
  name: 'SomeEntity',
  properties: p => ({
    user: () => p.manyToOne(User).mapToPk(),
  }),
});
```

./entities/User.ts

```
properties: {
  user: { entity: () => User, mapToPk: true },
},
```

For composite keys, this will give us ordered tuple representing the raw PKs, which is the internal format of composite PK:

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/User.ts

```
@ManyToOne(() => User, { mapToPk: true })
user: [string, string]; // [first_name, last_name]
```

./entities/User.ts

```
@ManyToOne(() => User, { mapToPk: true })
user: [string, string]; // [first_name, last_name]
```

./entities/User.ts

```
export const SomeEntity = defineEntity({
  name: 'SomeEntity',
  properties: p => ({
    user: () => p.manyToOne(User).mapToPk(),
  }),
});
```

./entities/User.ts

```
properties: {
  user: { entity: () => User, mapToPk: true },
},
```

## Formulas[​](#formulas "Direct link to Formulas")

`@Formula()` decorator can be used to map some SQL snippet to your entity. The SQL fragment can be as complex as you want and even include subselects.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Box.ts

```
@Formula('obj_length * obj_height * obj_width')
objectVolume?: number;
```

./entities/Box.ts

```
@Formula('obj_length * obj_height * obj_width')
objectVolume?: number;
```

./entities/Box.ts

```
export const Box = defineEntity({
  name: 'Box',
  properties: p => ({
    objectVolume: p.formula<number>('obj_length * obj_height * obj_width'),
  }),
});
```

./entities/Box.ts

```
properties: {
  objectVolume: { formula: 'obj_length * obj_height * obj_width' },
},
```

Formulas will be added to the select clause automatically. In case you are facing problems with `NonUniqueFieldNameException`, you can define the formula as a callback that will receive the entity alias in the parameter:

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Box.ts

```
@Formula(alias => `${alias}.obj_length * ${alias}.obj_height * ${alias}.obj_width`)
objectVolume?: number;
```

./entities/Box.ts

```
@Formula(alias => `${alias}.obj_length * ${alias}.obj_height * ${alias}.obj_width`)
objectVolume?: number;
```

./entities/Box.ts

```
export const Box = defineEntity({
  name: 'Box',
  properties: p => ({
    objectVolume: p.formula<number>((alias) => `${alias}.obj_length * ${alias}.obj_height * ${alias}.obj_width`),
  }),
});
```

./entities/Box.ts

```
properties: {
  objectVolume: { formula: alias => `${alias}.obj_length * ${alias}.obj_height * ${alias}.obj_width` },
},
```

## Indexes[​](#indexes "Direct link to Indexes")

We can define indexes via `@Index()` decorator, for unique indexes, we can use `@Unique()` decorator. We can use it either on entity class, or on entity property.

To define complex indexes, we can use index expressions. They allow us to specify the final `create index` query and an index name - this name is then used for index diffing, so the schema generator will only try to create it if it's not there yet, or remove it, if it's no longer defined in the entity. Index expressions are not bound to any property, rather to the entity itself (we can still define them on both entity and property level).

To define an index expression, you can either provide a raw SQL string, or use the expression callback to dynamically build the returned SQL.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Author.ts

```
@Entity()
@Index({ properties: ['name', 'age'] }) // compound index, with generated name
@Index({ name: 'custom_idx_name', properties: ['name'] }) // simple index, with custom name
@Unique({ properties: ['name', 'email'] })
export class Author {

  @Property()
  @Unique()
  email!: string;

  @Property()
  @Index() // generated name
  age?: number;

  @Index({ name: 'born_index' })
  @Property()
  born?: string;

  // Custom index using raw SQL string expression
  @Index({ name: 'custom_index_expr', expression: 'alter table `author` add index `custom_index_expr`(`title`)' })
  @Property()
  title!: string;

  // Custom index using expression callback
  // ${table.schema}, ${table.name}, and ${columns.title} return the unquoted identifiers.
  @Index({ name: 'custom_index_country1', expression: (table, columns, indexName) => `create index \`${indexName}\` on \`${table.schema}\`.\`${table.name}\` (\`${columns.country}\`)` })
  // Using quote helper to automatically quote identifiers.
  @Index({ name: 'custom_index_country2', expression: (table, columns, indexName) => quote`create index ${indexName} on ${table} (${columns.country})` })
  // Using raw function to automatically quote identifiers.
  @Index({ name: 'custom_index_country3', expression: (table, columns, indexName) => raw(`create index ?? on ?? (??)`, [indexName, table, columns.country]) })
  @Property()
  country!: string;

}
```

./entities/Author.ts

```
@Entity()
@Index({ properties: ['name', 'age'] }) // compound index, with generated name
@Index({ name: 'custom_idx_name', properties: ['name'] }) // simple index, with custom name
@Unique({ properties: ['name', 'email'] })
export class Author {

  @Property()
  @Unique()
  email!: string;

  @Property()
  @Index() // generated name
  age?: number;

  @Index({ name: 'born_index' })
  @Property()
  born?: string;

  // Custom index using raw SQL string expression
  @Index({ name: 'custom_index_expr', expression: 'alter table `author` add index `custom_index_expr`(`title`)' })
  @Property()
  title!: string;

  // Custom index using expression callback
  // ${table.schema}, ${table.name}, and ${columns.title} return the unquoted identifiers.
  @Index({ name: 'custom_index_country1', expression: (table, columns, indexName) => `create index \`${indexName}\` on \`${table.schema}\`.\`${table.name}\` (\`${columns.country}\`)` })
  // Using quote helper to automatically quote identifiers.
  @Index({ name: 'custom_index_country2', expression: (table, columns, indexName) => quote`create index ${indexName} on ${table} (${columns.country})` })
  // Using raw function to automatically quote identifiers.
  @Index({ name: 'custom_index_country3', expression: (table, columns, indexName) => raw(`create index ?? on ?? (??)`, [indexName, table, columns.country]) })
  @Property()
  country!: string;

}
```

./entities/Author.ts

```
export const Author = defineEntity({
  name: 'Author',
  properties: p => ({
    email: p.string().unique(),
    age: p.number().nullable().index(),
    born: p.date().nullable().index('born_index'),
    title: p.string(),
    country: p.string(),
  }),
  indexes: [
    { properties: ['name', 'age'] }, // compound index, with generated name
    { name: 'custom_idx_name', properties: ['name'] }, // simple index, with custom name
    // Custom index using expression callback
    // ${table.schema}, ${table.name}, and ${columns.title} return the unquoted identifiers.
    { name: 'custom_index_country1', expression: (table, columns, indexName) => `create index \`${indexName}\` on \`${table.schema}\`.\`${table.name}\` (\`${columns.country}\`)` },
    // Using quote helper to automatically quote identifiers.
    { name: 'custom_index_country2', expression: (table, columns, indexName) => quote`create index ${indexName} on ${table} (${columns.country})` },
    // Using raw function to automatically quote identifiers.
    { name: 'custom_index_country3', expression: (table, columns, indexName) => raw(`create index ?? on ?? (??)`, [indexName, table, columns.country]) },
  ],
  uniques: [
    { properties: ['name', 'email'] },
  ],
});
```

./entities/Author.ts

```
export const AuthorSchema = new EntitySchema<Author, CustomBaseEntity>({
  class: Author,
  indexes: [
    { properties: ['name', 'age'] }, // compound index, with generated name
    { name: 'custom_idx_name', properties: ['name'] }, // simple index, with custom name
    // Custom index using raw SQL string expression
    { name: 'custom_index_expr', expression: 'alter table `author` add index `custom_index_expr`(`title`)' },
    // Custom index using expression callback
    // ${table.schema}, ${table.name}, and ${columns.title} return the unquoted identifiers.
    { name: 'custom_index_country1', expression: (table, columns, indexName) => `create index \`${indexName}\` on \`${table.schema}\`.\`${table.name}\` (\`${columns.country}\`)` },
    // Using quote helper to automatically quote identifiers.
    { name: 'custom_index_country2', expression: (table, columns, indexName) => quote`create index ${indexName} on ${table} (${columns.country})` },
    // Using raw function to automatically quote identifiers.
    { name: 'custom_index_country3', expression: (table, columns, indexName) => raw(`create index ?? on ?? (??)`, [indexName, table, columns.country]) },
  ],
  uniques: [
    { properties: ['name', 'email'] },
  ],
  properties: {
    email: { type: 'string', unique: true }, // generated name
    age: { type: 'number', nullable: true, index: true }, // generated name
    born: { type: 'date', nullable: true, index: 'born_index' },
    title: { type: 'string' },
  },
});
```

## Check constraints[​](#check-constraints "Direct link to Check constraints")

We can define check constraints via `@Check()` decorator. We can use it either on entity class, or on entity property. It has a required `expression` property, that can be either a string or a callback, that receives map of property names to column names. Note that we need to use the generic type argument if we want TypeScript suggestions for the property names.

> Check constraints are currently supported only in postgres driver.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Book.ts

```
@Entity()
// with generated name based on the table name
@Check({ expression: 'price1 >= 0' })
// with explicit name
@Check({ name: 'foo', expression: columns => `${columns.price1} >= 0` })
// with explicit type argument we get autocomplete on `columns`
@Check<FooEntity>({ expression: columns => `${columns.price1} >= 0` })
export class Book {

  @PrimaryKey()
  id!: number;

  @Property()
  price1!: number;

  @Property()
  @Check({ expression: 'price2 >= 0' })
  price2!: number;

  @Property({ check: columns => `${columns.price3} >= 0` })
  price3!: number;

}
```

./entities/Book.ts

```
@Entity()
// with generated name based on the table name
@Check({ expression: 'price1 >= 0' })
// with explicit name
@Check({ name: 'foo', expression: columns => `${columns.price1} >= 0` })
// with explicit type argument we get autocomplete on `columns`
@Check<FooEntity>({ expression: columns => `${columns.price1} >= 0` })
export class Book {

  @PrimaryKey()
  id!: number;

  @Property()
  price1!: number;

  @Property()
  @Check({ expression: 'price2 >= 0' })
  price2!: number;

  @Property({ check: columns => `${columns.price3} >= 0` })
  price3!: number;

}
```

./entities/Book.ts

```
export const Book = defineEntity({
  name: 'Book',
  properties: p => ({
    id: p.number().primary(),
    price1: p.number(),
    price2: p.number(),
    price3: p.number(),
  }),
  checks: [
    { expression: 'price1 >= 0' },
    { name: 'foo', expression: columns => `${columns.price1} >= 0` },
    { expression: columns => `${columns.price1} >= 0` },
    { propertyName: 'price2', expression: 'price2 >= 0' },
    { propertyName: 'price3', expression: columns => `${columns.price3} >= 0` },
  ],
});
```

./entities/Book.ts

```
export const BookSchema = new EntitySchema({
  class: Book,
  checks: [
    { expression: 'price1 >= 0' },
    { name: 'foo', expression: columns => `${columns.price1} >= 0` },
    { expression: columns => `${columns.price1} >= 0` },
    { propertyName: 'price2', expression: 'price2 >= 0' },
    { propertyName: 'price3', expression: columns => `${columns.price3} >= 0` },
  ],
  properties: {
    id: { type: 'number', primary: true },
    price1: { type: 'number' },
    price2: { type: 'number' },
    price3: { type: 'number' },
  },
});
```

## Custom Types[​](#custom-types "Direct link to Custom Types")

We can define custom types by extending `Type` abstract class. It has 4 optional methods:

* `convertToDatabaseValue(value: any, platform: Platform): any`

  Converts a value from its JS representation to its database representation of this type.

* `convertToJSValue(value: any, platform: Platform): any`

  Converts a value from its database representation to its JS representation of this type.

* `toJSON(value: any, platform: Platform): any`

  Converts a value from its JS representation to its serialized JSON form of this type. By default, converts to the database value.

* `getColumnType(prop: EntityProperty, platform: Platform): string`

  Gets the SQL declaration snippet for a field of this type.

More information can be found in [Custom Types](https://mikro-orm.io/docs/custom-types.md) section.

## Lazy scalar properties[​](#lazy-scalar-properties "Direct link to Lazy scalar properties")

We can mark any property as `lazy: true` to omit it from the select clause. This can be handy for properties that are too large, and you want to have them available only sometimes, like a full text of an article.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Book.ts

```
@Property({ columnType: 'text', lazy: true })
text: string;
```

./entities/Book.ts

```
@Property({ columnType: 'text', lazy: true })
text: string;
```

./entities/Book.ts

```
export const Book = defineEntity({
  name: 'Book',
  properties: p => ({
    text: p.text().lazy(),
  }),
});
```

./entities/Book.ts

```
properties: {
  text: { columnType: 'text', lazy: true },
}
```

We can use `populate` parameter to load them.

```
const b1 = await em.find(Book, 1); // this will omit the `text` property
const b2 = await em.find(Book, 1, { populate: ['text'] }); // this will load the `text` property
```

> If the entity is already loaded, and you need to populate a lazy scalar property, you might need to pass `refresh: true` in the `FindOptions`.

### `ScalarReference` wrapper[​](#scalarreference-wrapper "Direct link to scalarreference-wrapper")

Similarly to the `Reference` wrapper, we can also wrap lazy scalars with `Ref` into a `ScalarReference` object. The `Ref` type automatically resolves to `ScalarReference` for non-object types, so the below is correct:

```
@Property({ lazy: true, ref: true })
passwordHash!: Ref<string>;
```

```
const user = await em.findOne(User, 1);
const passwordHash = await user.passwordHash.load();
```

For object-like types, if you choose to use the reference wrappers, you should use the `ScalarRef<T>` type explicitly. For example, you might want to lazily load a large JSON value:

```
@Property({ type: 'json', nullable: true, lazy: true, ref: true })
// ReportParameters is an object type, imagine it defined elsewhere.
reportParameters!: ScalarRef<ReportParameters | null>; 
```

Keep in mind that once a scalar value is managed through a `ScalarReference`, accessing it through MikroORM managed objects will always return the `ScalarReference` wrapper. That can be confusing in case the property is also `nullable`, since the `ScalarReference` will always be truthy. In such cases, you should inform the type system of the nullability of the property through `ScalarReference<T>`'s type parameter as demonstrated above. Below is an example of how it all works:

```
// Say Report of id "1" has no reportParameters in the Database.
const report = await em.findOne(Report, 1);
if (report.reportParameters) {
  // Logs Ref<?>, not the actual value. **Would always run***.
  console.log(report.reportParameters);
  //@ts-expect-error $/.get() is not available until the reference has been loaded.
  // const mistake = report.reportParameters.$
}
const populatedReport = await em.populate(report, ['reportParameters']);
// Logs `null`
console.log(populatedReport.reportParameters.$); 
```

## Private property accessors[​](#private-property-accessors "Direct link to Private property accessors")

When using a private property backed by a public get/set pair, use the `accessor` option to point to the other side.

> The `fieldName` will be inferred based on the accessor name unless specified explicitly.

If the `accessor` option points to something, the ORM will use the backing property directly:

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/User.ts

```
@Entity()
export class User {
  @PrimaryKey()
  id!: number;

  // the ORM will use the backing field directly
  @Property({ accessor: 'email' })
  private _email: string;

  get email() {
    return this._email;
  }

  set email(email: string) {
    return this._email;
  }
}
```

./entities/User.ts

```
@Entity()
export class User {
  @PrimaryKey()
  id!: number;
  
  // the ORM will use the backing field directly
  @Property({ accessor: 'email' })
  private _email: string;

  get email() {
    return this._email;
  }

  set email(email: string) {
    return this._email;
  }
}
```

./entities/User.ts

```
export class User {
  id!: number;
  private _email!: unknown;

  get email(): unknown {
    return this._email;
  }

  set email(email: unknown) {
    this._email = email;
  }
}

export const UserSchema = defineEntity({
  class: User,
  properties: {
    id: p.integer().primary(),
    // the ORM will use the backing field directly
    email: p.string().accessor('_email'),
  },
});
```

./entities/User.ts

```
export class User {
  id!: string;
  private _email!: string;

  get email() {
    return this._email;
  }

  set email(email: string) {
    return this._email;
  }
}

export const UserSchema = new EntitySchema({
  class: User,
  properties: {
    id: { type: 'integer', primary: true },
    // the ORM will use the backing property internally
    email: { type: 'string', accessor: '_email' },
  },
});
```

If you want the ORM to use the accessor internally (e.g. for hydration or change tracking), use `accessor: true` on the get/set property instead. This is handy if you want to use a **native private property** for the backing field.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/User.ts

```
@Entity({ forceConstructor: true })
export class User {
  @PrimaryKey()
  id!: number;

  #email!: string;

  // the ORM will use the accessor internally
  @Property({ accessor: true })
  get email() {
    return this.#email;
  }

  set email(email: string) {
    return this.#email;
  }
}
```

./entities/User.ts

```
@Entity({ forceConstructor: true })
export class User {
  @PrimaryKey()
  id!: number;

  #email!: string;

  // the ORM will use the accessor internally
  @Property({ accessor: true })
  get email() {
    return this.#email;
  }

  set email(email: string) {
    return this.#email;
  }
}
```

./entities/User.ts

```
export class User {
  id!: string;
  #email!: string;

  get email() {
    return this.#email;
  }

  set email(email: string) {
    return this.#email;
  }
}

export const UserSchema = defineEntity({
  class: User,
  // constructors are required for native private fields
  forceConstructor: true,
  properties: {
    id: p.integer().primary(),
    // the ORM will use the accessor internally
    email: p.string().accessor(),
  },
});
```

./entities/User.ts

```
export class User {
  id!: string;
  #email!: string;

  get email() {
    return this.#email;
  }

  set email(email: string) {
    return this.#email;
  }
}

export const UserSchema = new EntitySchema({
  class: User,
  // constructors are required for native private fields
  forceConstructor: true,
  properties: {
    id: { type: 'integer', primary: true },
    // the ORM will use the accessor internally
    email: { type: 'string', accessor: true },
  },
});
```

## Virtual Properties[​](#virtual-properties "Direct link to Virtual Properties")

We can define our properties as virtual, either as a method, or via JavaScript `get/set`.

Following example defines User entity with `firstName` and `lastName` database fields, that are both hidden from the serialized response, replaced with virtual properties `fullName` (defined as a classic method) and `fullName2` (defined as a JavaScript getter).

> For JavaScript getter you need to provide `{ persist: false }` option otherwise the value would be stored in the database.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/User.ts

```
@Entity()
export class User {

  [HiddenProps]?: 'firstName' | 'lastName';

  @Property({ hidden: true })
  firstName!: string;

  @Property({ hidden: true })
  lastName!: string;

  @Property({ name: 'fullName' })
  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  @Property({ persist: false })
  get fullName2() {
    return `${this.firstName} ${this.lastName}`;
  }

}
```

./entities/User.ts

```
@Entity()
export class User {

  [HiddenProps]?: 'firstName' | 'lastName';

  @Property({ hidden: true })
  firstName!: string;

  @Property({ hidden: true })
  lastName!: string;

  @Property({ name: 'fullName' })
  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  @Property({ persist: false })
  get fullName2() {
    return `${this.firstName} ${this.lastName}`;
  }

}
```

./entities/User.ts

```
export class User {

  [HiddenProps]?: 'firstName' | 'lastName';

  firstName!: string;
  lastName!: string;

  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  get fullName2() {
    return `${this.firstName} ${this.lastName}`;
  }
}

export const UserSchema = defineEntity({
  class: User,
  name: 'User',
  properties: p => ({
    firstName: p.string().hidden(),
    lastName: p.string().hidden(),
    fullName: p.type('method').persist(false).getter().getterName('getFullName'),
    fullName2: p.type('method').persist(false).getter(),
  }),
});
```

./entities/User.ts

```
export class User {

  [HiddenProps]?: 'firstName' | 'lastName';

  firstName!: string;
  lastName!: string;

  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  get fullName2() {
    return `${this.firstName} ${this.lastName}`;
  }
}

properties: {
  firstName: { type: String, hidden: true },
  lastName: { type: String, hidden: true },
  fullName: { type: 'method', persist: false, getter: true, getterName: 'getFullName' },
  fullName2: { type: 'method', persist: false, getter: true },
}
```

```
const repo = em.getRepository(User);
const author = repo.create({ firstName: 'Jon', lastName: 'Snow' });

console.log(author.getFullName()); // 'Jon Snow'
console.log(author.fullName2); // 'Jon Snow'
console.log(wrap(author).toJSON()); // { fullName: 'Jon Snow', fullName2: 'Jon Snow' }
```

## Entity file names[​](#entity-file-names "Direct link to Entity file names")

Starting with MikroORM 4.2, there is no limitation for entity file names. It is now also possible to define multiple entities in a single file using folder based discovery.

## Using custom base entity[​](#using-custom-base-entity "Direct link to Using custom base entity")

We can define our own base entity with properties that are required on all entities, like primary key and created/updated time. Single table inheritance is also supported.

Read more about this topic in [Inheritance Mapping](https://mikro-orm.io/docs/inheritance-mapping.md) section.

> If you are initializing the ORM via `entities` option, you need to specify all your base entities as well.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/CustomBaseEntity.ts

```
import { v4 } from 'uuid';

export abstract class CustomBaseEntity {

  @PrimaryKey()
  uuid = v4();

  @Property()
  createdAt = new Date();

  @Property({ onUpdate: () => new Date() })
  updatedAt = new Date();

}
```

./entities/CustomBaseEntity.ts

```
import { v4 } from 'uuid';

export abstract class CustomBaseEntity {

  @PrimaryKey()
  uuid = v4();

  @Property()
  createdAt = new Date();

  @Property({ onUpdate: () => new Date() })
  updatedAt = new Date();

}
```

./entities/CustomBaseEntity.ts

```
const p = defineEntity.properties;
const CustomBaseProperties = {
  uuid: p.uuid().primary().onCreate(() => v4()),
  createdAt: p.datetime()
    .onCreate(() => new Date())
    .nullable(),
  updatedAt: p.datetime()
    .onCreate(() => new Date())
    .onUpdate(() => new Date())
    .nullable(),
}
```

./entities/CustomBaseEntity.ts

```
import { v4 } from 'uuid';

export interface CustomBaseEntity {
  uuid: string;
  createdAt: Date;
  updatedAt: Date;
}

export const schema = new EntitySchema<CustomBaseEntity>({
  name: 'CustomBaseEntity',
  abstract: true,
  properties: {
    uuid: { type: 'uuid', onCreate: () => v4(), primary: true },
    createdAt: { type: 'Date', onCreate: () => new Date(), nullable: true },
    updatedAt: { type: 'Date', onCreate: () => new Date(), onUpdate: () => new Date(), nullable: true },
  },
});
```

There is a special case, when we need to annotate the base entity - if we are using folder based discovery, and the base entity is not using any decorators (e.g. it does not define any decorated property). In that case, we need to mark it as abstract:

```
@Entity({ abstract: true })
export abstract class CustomBaseEntity {
  // ...
}
```

## SQL Generated columns[​](#sql-generated-columns "Direct link to SQL Generated columns")

To use generated columns, you can either use the `generated` option, or specify it as part of the `columnType`:

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/User.ts

```
@Entity()
export class User {

  @PrimaryKey()
  id!: number;

  @Property({ length: 50 })
  firstName!: string;

  @Property({ length: 50 })
  lastName!: string;

  @Property<User>({ length: 100, generated: cols => `(concat(${cols.firstName}, ' ', ${cols.lastName})) stored` })
  fullName!: string & Opt;

  @Property({ columnType: `varchar(100) generated always as (concat(first_name, ' ', last_name)) virtual` })
  fullName2!: string & Opt;

}
```

./entities/User.ts

```
@Entity()
export class User {

  @PrimaryKey()
  id!: number;

  @Property({ length: 50 })
  firstName!: string;

  @Property({ length: 50 })
  lastName!: string;

  @Property<User>({ length: 100, generated: cols => `(concat(${cols.firstName}, ' ', ${cols.lastName})) stored` })
  fullName!: string & Opt;

  @Property({ columnType: `varchar(100) generated always as (concat(first_name, ' ', last_name)) virtual` })
  fullName2!: string & Opt;

}
```

./entities/User.ts

```
export const User = defineEntity({
  name: 'User',
  properties: p => ({
    id: p.integer().primary(),
    firstName: p.string().length(50),
    lastName: p.string().length(50),
    fullName: p.string()
      .length(100)
      .generated(cols => `(concat(${cols.firstName}, ' ', ${cols.lastName})) stored`),
    fullName2: p.string()
      .length(100)
      .columnType(`varchar(100) generated always as (concat(first_name, ' ', last_name)) virtual`),
  }),
});
```

./entities/User.ts

```
export interface IUser {
  id: number;
  firstName: string;
  lastName: string;
  fullName: string & Opt;
  fullName2: string & Opt;
}

export const User = new EntitySchema<IUser>({
  name: 'User',
  properties: {
    id: { type: 'number', primary: true },
    firstName: { type: 'string', length: 50 },
    lastName: { type: 'string', length: 50 },
    fullName: {
      type: 'string',
      length: 100,
      generated: cols => `(concat(${cols.firstName}, ' ', ${cols.lastName})) stored`,
    },
    fullName2: {
      type: 'string',
      columnType: `varchar(100) generated always as (concat(first_name, ' ', last_name)) virtual`,
    },
  },
});
```

To use a generated identity column in PostgreSQL, set the `generated` option to `identity`:

> To allow providing the value explicitly, use `generated: 'by default as identity'`.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/User.ts

```
@Entity()
export class User {

  @PrimaryKey({ generated: 'identity' })
  id!: number;

}
```

./entities/User.ts

```
@Entity()
export class User {

  @PrimaryKey({ generated: 'identity' })
  id!: number;

}
```

./entities/User.ts

```
export const User = defineEntity({
  name: 'User',
  properties: p => ({
    id: p.integer().primary().generated('identity'),
  }),
});
```

./entities/User.ts

```
export interface IUser {
  id: number;
}

export const User = new EntitySchema<IUser>({
  name: 'User',
  properties: {
    id: { type: 'number', primary: true, generated: 'identity' },
  },
});
```

## Examples of entity definition with various primary keys[​](#examples-of-entity-definition-with-various-primary-keys "Direct link to Examples of entity definition with various primary keys")

### Using id as primary key (SQL drivers)[​](#using-id-as-primary-key-sql-drivers "Direct link to Using id as primary key (SQL drivers)")

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Book.ts

```
@Entity()
export class Book {

  @PrimaryKey()
  id!: number; // string is also supported

  @Property()
  title!: string;

  @ManyToOne(() => Author)
  author!: Author;

  @ManyToOne(() => Publisher, { ref: true, nullable: true })
  publisher?: Ref<Publisher>;

}
```

./entities/Book.ts

```
@Entity()
export class Book {

  @PrimaryKey()
  id!: number; // string is also supported

  @Property()
  title!: string;

  @ManyToOne()
  author!: Author;

  @ManyToOne()
  publisher?: Ref<Publisher>;

}
```

./entities/Book.ts

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

./entities/Book.ts

```
export interface Book {
  id: number;
  title: string;
  author: Author;
}

export const BookSchema = new EntitySchema<Book>({
  name: 'Book',
  properties: {
    id: { type: Number, primary: true },
    title: { type: String },
    author: { kind: 'm:1', entity: 'Author' },
    publisher: { kind: 'm:1', entity: 'Publisher', ref: true, nullable: true },
  },
});
```

### Using UUID as primary key (SQL drivers)[​](#using-uuid-as-primary-key-sql-drivers "Direct link to Using UUID as primary key (SQL drivers)")

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Book.ts

```
import { v4 } from 'uuid';

@Entity()
export class Book {

  @PrimaryKey()
  uuid = v4();

  @Property()
  title!: string;

  @ManyToOne(() => Author)
  author!: Author;

}
```

./entities/Book.ts

```
import { v4 } from 'uuid';

@Entity()
export class Book {

  @PrimaryKey()
  uuid = v4();

  @Property()
  title!: string;

  @ManyToOne()
  author!: Author;

}
```

./entities/Book.ts

```
export const Book = defineEntity({
  name: 'Book',
  properties: p => ({
    uuid: p.uuid().primary().onCreate(() => v4()),
    title: p.string(),
    author: () => p.manyToOne(Author),
  }),
});
```

./entities/Book.ts

```
export interface IBook {
  uuid: string;
  title: string;
  author: Author;
}

export const Book = new EntitySchema<IBook>({
  name: 'Book',
  properties: {
    uuid: { type: 'uuid', onCreate: () => v4(), primary: true },
    title: { type: 'string' },
    author: { entity: () => Author, kind: 'm:1' },
  },
});
```

### Using PostgreSQL built-in [gen\_random\_uuid](https://www.postgresql.org/docs/current/functions-uuid.html) function as primary key[​](#using-postgresql-built-in-gen_random_uuid-function-as-primary-key "Direct link to using-postgresql-built-in-gen_random_uuid-function-as-primary-key")

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Book.ts

```
@Entity()
export class Book {

  @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
  uuid: string;

  @Property()
  title!: string;

  @ManyToOne(() => Author)
  author!: Author;

}
```

./entities/Book.ts

```
@Entity()
export class Book {

  @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
  uuid: string;

  @Property()
  title!: string;

  @ManyToOne()
  author!: Author;

}
```

./entities/Book.ts

```
export const Book = defineEntity({
  name: 'Book',
  properties: p => ({
    uuid: p.uuid().primary().defaultRaw('gen_random_uuid()'),
    title: p.string(),
    author: () => p.manyToOne(Author),
  }),
});
```

./entities/Book.ts

```
export class Book {
  uuid: string;
  title!: string;
  author!: Author;
}

export const BookSchema = new EntitySchema<Book>({
  class: Book,
  properties: {
    uuid: { type: 'uuid', defaultRaw: 'gen_random_uuid()', primary: true },
    title: { type: 'string' },
    author: { entity: () => Author, kind: 'm:1' },
  },
});
```

### Using BigInt as primary key (MySQL and PostgreSQL)[​](#using-bigint-as-primary-key-mysql-and-postgresql "Direct link to Using BigInt as primary key (MySQL and PostgreSQL)")

Since v6, `bigint`s are represented by the native `BigInt` type, and as such, they don't require explicit type in the decorator options:

```
@PrimaryKey()
id: bigint;
```

You can also specify the target type you want your bigints to be mapped to:

```
@PrimaryKey({ type: new BigIntType('bigint') })
id1: bigint;

@PrimaryKey({ type: new BigIntType('string') })
id2: string;

@PrimaryKey({ type: new BigIntType('number') })
id3: number;
```

> JavaScript cannot represent all the possible values of a `bigint` when mapping to the `number` type - only values up to `Number.MAX_SAFE_INTEGER` (2^53 - 1) are safely supported.

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/CustomBaseEntity.ts

```
@Entity()
export class Book {

  @PrimaryKey()
  id: bigint;

}
```

./entities/CustomBaseEntity.ts

```
@Entity()
export class Book {

  @PrimaryKey()
  id: bigint;

}
```

./entities/CustomBaseEntity.ts

```
const SomeEntity = defineEntity({
  name: 'SomeEntity',
  properties: p => ({
    id: p.bigint().primary(),
  }),
});
```

./entities/CustomBaseEntity.ts

```
properties: {
  id: { type: 'bigint' },
},
```

If you want to use native `bigint`s, read the following guide: [Using native BigInt PKs](https://mikro-orm.io/docs/using-bigint-pks.md).

### Example of Mongo entity[​](#example-of-mongo-entity "Direct link to Example of Mongo entity")

* reflect-metadata
* ts-morph
* defineEntity
* EntitySchema

./entities/Book.ts

```
@Entity()
export class Book {

  @PrimaryKey()
  _id!: ObjectId;

  @SerializedPrimaryKey()
  id!: string; // string variant of PK, will be handled automatically

  @Property()
  title!: string;

  @ManyToOne(() => Author)
  author!: Author;

}
```

./entities/Book.ts

```
@Entity()
export class Book {

  @PrimaryKey()
  _id!: ObjectId;

  @SerializedPrimaryKey()
  id!: string; // string variant of PK, will be handled automatically

  @Property()
  title!: string;

  @ManyToOne()
  author!: Author;

}
```

./entities/Book.ts

```
export const Book = defineEntity({
  name: 'Book',
  properties: p => ({
    _id: p.type(ObjectId).primary(),
    id: p.string().serializedPrimaryKey(),
    title: p.string(),
  }),
});
```

./entities/Book.ts

```
export interface IBook {
  _id: ObjectId;
  id: string;
  title: string;
  author: Author;
}

export const Book = new EntitySchema<IBook>({
  name: 'Book',
  properties: {
    _id: { type: 'ObjectId', primary: true },
    id: { type: String, serializedPrimaryKey: true },
    title: { type: String },
  },
});
```

### Using MikroORM's BaseEntity (previously WrappedEntity)[​](#using-mikroorms-baseentity-previously-wrappedentity "Direct link to Using MikroORM's BaseEntity (previously WrappedEntity)")

From v4 `BaseEntity` class is provided with `init`, `isInitialized`, `assign` and other methods that are otherwise available via the `wrap()` helper.

> Usage of the `BaseEntity` is optional.

```
import { BaseEntity } from '@mikro-orm/core';

@Entity()
export class Book extends BaseEntity {

  @PrimaryKey()
  id!: number;

  @Property()
  title!: string;

  @ManyToOne()
  author!: Author;

}

const book = new Book();
console.log(book.isInitialized()); // true
```

Having the entities set up, we can now start [using entity manager](https://mikro-orm.io/docs/entity-manager.md) and [repositories](https://mikro-orm.io/docs/repositories.md) as described in following sections.