Multi-ORM Architecture
Ever Gauzy uniquely supports multiple ORMs simultaneously — TypeORM, MikroORM, and Knex — allowing developers to choose the best tool for each use case while sharing the same entity definitions and database.
Architecture Overview
ORM Selection
The active ORM is configured via the DB_ORM environment variable:
# .env
DB_ORM=typeorm # Options: typeorm | mikro-orm
When to Use Each ORM
| ORM | Best For | Characteristics |
|---|---|---|
| TypeORM | General CRUD, complex relations, migrations | Active Record + Data Mapper patterns, eager loading |
| MikroORM | Strict data integrity, unit of work | Identity map, automatic change tracking, stricter metadata |
| Knex | Raw SQL, complex aggregations, high-performance queries | Direct SQL control, query builder, raw performance |
Multi-ORM Decorators
The platform uses a custom Multi-ORM decorator system that generates metadata for both TypeORM and MikroORM from a single entity definition.
Entity Definition
import { MultiORMEntity, MultiORMColumn, MultiORMManyToOne } from "@gauzy/core";
@MultiORMEntity("employee")
export class Employee extends TenantOrganizationBaseEntity {
@MultiORMColumn()
firstName: string;
@MultiORMColumn()
lastName: string;
@MultiORMColumn({ nullable: true })
startedWorkOn?: Date;
@MultiORMManyToOne(() => User, {
nullable: false,
onDelete: "CASCADE",
})
user: IUser;
@MultiORMColumn({ relationId: true })
userId: string;
}
Decorator Mapping
| Multi-ORM Decorator | TypeORM Equivalent | MikroORM Equivalent |
|---|---|---|
@MultiORMEntity() | @Entity() | @Entity() |
@MultiORMColumn() | @Column() | @Property() |
@MultiORMManyToOne() | @ManyToOne() | @ManyToOne() |
@MultiORMOneToMany() | @OneToMany() | @OneToMany() |
@MultiORMManyToMany() | @ManyToMany() | @ManyToMany() |
@MultiORMOneToOne() | @OneToOne() | @OneToOne() |
Conditional Decorator Application
Decorators are conditionally applied based on the active ORM:
export function MultiORMColumn(options?: ColumnOptions): PropertyDecorator {
return (target, propertyKey) => {
if (getDBORM() === "typeorm") {
Column(options)(target, propertyKey);
}
if (getDBORM() === "mikro-orm") {
Property(mapToMikroORMOptions(options))(target, propertyKey);
}
};
}
This prevents metadata conflicts when only one ORM is active at runtime.
Base Entity Classes
BaseEntity
The root entity class providing common fields:
export abstract class BaseEntity {
@MultiORMColumn({ primary: true })
id: string;
@MultiORMColumn({ createDate: true })
createdAt: Date;
@MultiORMColumn({ updateDate: true })
updatedAt: Date;
@MultiORMColumn({ nullable: true })
isActive: boolean;
@MultiORMColumn({ nullable: true })
isArchived: boolean;
}
TenantBaseEntity
Adds tenant scoping:
export abstract class TenantBaseEntity extends BaseEntity {
@MultiORMManyToOne(() => Tenant, { nullable: false })
tenant: ITenant;
@MultiORMColumn({ relationId: true })
tenantId: string;
}
TenantOrganizationBaseEntity
Adds organization scoping (most common base class):
export abstract class TenantOrganizationBaseEntity extends TenantBaseEntity {
@MultiORMManyToOne(() => Organization, { nullable: true })
organization?: IOrganization;
@MultiORMColumn({ relationId: true, nullable: true })
organizationId?: string;
}