Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Mikro-ORM
https://github.com/mikro-orm/mikro-orm
Admin
Mikro ORM is a TypeScript ORM for Node.js supporting MongoDB, MySQL, MariaDB, PostgreSQL, and
...
Tokens:
1,586
Snippets:
16
Trust Score:
8.9
Update:
1 week ago
Context
Skills
Chat
Benchmark
80.9
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# MikroORM MikroORM is a TypeScript ORM for Node.js based on the Data Mapper, Unit of Work, and Identity Map patterns. It provides a powerful yet developer-friendly API for working with relational databases (PostgreSQL, MySQL, MariaDB, SQLite, MSSQL, Oracle) and MongoDB. The ORM features automatic change tracking, lazy/eager loading, cascading operations, migrations, and a type-safe query builder. The library supports multiple approaches for defining entities: the recommended `defineEntity` helper with full TypeScript type inference, traditional decorators (both legacy and ES spec), and low-level `EntitySchema`. MikroORM v7 introduces zero runtime dependencies in core, native ESM support, Kysely-based query execution, type-safe QueryBuilder with alias tracking, and pre-compiled functions for edge runtimes. ## Installation Install core package and database driver for your target database. ```bash # PostgreSQL npm install @mikro-orm/core @mikro-orm/postgresql # MySQL/MariaDB npm install @mikro-orm/core @mikro-orm/mysql # SQLite npm install @mikro-orm/core @mikro-orm/sqlite # MongoDB npm install @mikro-orm/core @mikro-orm/mongodb ``` ## ORM Initialization Initialize MikroORM with configuration options including entities, database connection, and optional extensions. ```typescript import { MikroORM, defineConfig } from '@mikro-orm/postgresql'; import { Migrator } from '@mikro-orm/migrations'; import { SeedManager } from '@mikro-orm/seeder'; // Using defineConfig for type-safe configuration export default defineConfig({ entities: [Author, Book, Publisher, BookTag], dbName: 'my-database', host: 'localhost', port: 5432, user: 'postgres', password: 'secret', debug: true, extensions: [Migrator, SeedManager], migrations: { path: './dist/migrations', pathTs: './src/migrations', }, }); // Initialize ORM const orm = await MikroORM.init(); console.log(orm.em); // EntityManager instance // Or synchronous initialization (limited features) const orm2 = new MikroORM({ entities: [Author, Book], dbName: 'my-db', }); // Cleanup await orm.close(); ``` ## Entity Definition with defineEntity Define entities using the `defineEntity` helper with full type inference and optional class extension for custom methods. ```typescript import { defineEntity, p, Collection, InferEntity } from '@mikro-orm/core'; // Define schema with properties const AuthorSchema = defineEntity({ name: 'Author', properties: { id: p.integer().primary(), firstName: p.string(), lastName: p.string(), email: p.string().unique(), age: p.integer().nullable(), createdAt: p.datetime().onCreate(() => new Date()), updatedAt: p.datetime().onUpdate(() => new Date()), books: () => p.oneToMany(Book).mappedBy('author'), }, }); // Extend with custom class for domain methods export class Author extends AuthorSchema.class { fullName() { return `${this.firstName} ${this.lastName}`; } } AuthorSchema.setClass(Author); // Book entity with relationships const BookSchema = defineEntity({ name: 'Book', properties: { id: p.integer().primary(), title: p.string(), price: p.float(), author: () => p.manyToOne(Author).inversedBy('books'), tags: () => p.manyToMany(BookTag).inversedBy('books'), publisher: () => p.manyToOne(Publisher).nullable(), }, }); export class Book extends BookSchema.class {} BookSchema.setClass(Book); // Pure defineEntity without class (simpler but less ergonomic types) export const BookTag = defineEntity({ name: 'BookTag', properties: { id: p.integer().primary(), name: p.string(), books: () => p.manyToMany(Book).mappedBy('tags'), }, }); export type IBookTag = InferEntity<typeof BookTag>; ``` ## Entity Definition with Decorators Define entities using TypeScript decorators with explicit type annotations. ```typescript import { Entity, PrimaryKey, Property, ManyToOne, OneToMany, ManyToMany, Collection } from '@mikro-orm/decorators/legacy'; @Entity() export class Author { @PrimaryKey() id!: number; @Property() firstName!: string; @Property() lastName!: string; @Property({ unique: true }) email!: string; @Property({ nullable: true }) age?: number; @Property({ version: true }) version!: number; @OneToMany(() => Book, book => book.author, { orphanRemoval: true }) books = new Collection<Book>(this); @Property({ onCreate: () => new Date() }) createdAt: Date = new Date(); } @Entity() export class Book { @PrimaryKey() id!: number; @Property() title!: string; @Property({ type: 'decimal', precision: 10, scale: 2 }) price!: string; @ManyToOne(() => Author) author!: Author; @ManyToMany(() => BookTag) tags = new Collection<BookTag>(this); } ``` ## EntityManager - Creating and Persisting Entities Use EntityManager to create, persist, and flush entities to the database with automatic change tracking. ```typescript import { MikroORM, RequestContext } from '@mikro-orm/postgresql'; const orm = await MikroORM.init({ /* config */ }); const em = orm.em; // Create entities with em.create() - auto-persisted by default const author = em.create(Author, { firstName: 'Jon', lastName: 'Snow', email: 'jon@wall.st', age: 25, }); const book1 = em.create(Book, { title: 'My Life on The Wall, part 1', price: 29.99, author, // relation assignment }); const book2 = em.create(Book, { title: 'My Life on The Wall, part 2', price: 34.99, author, tags: [{ name: 'Fantasy' }, { name: 'Adventure' }], // nested creation }); // Flush all changes to database in single transaction await em.flush(); console.log(author.id); // populated after flush console.log(book1.id); // Update existing entity - changes tracked automatically const loadedAuthor = await em.findOneOrFail(Author, author.id); loadedAuthor.age = 26; await em.flush(); // UPDATE query issued // Manual persist for entities created with constructor const newBook = new Book(); newBook.title = 'Part 3'; newBook.author = loadedAuthor; em.persist(newBook); await em.flush(); // Express middleware for request-scoped EntityManager app.use((req, res, next) => { RequestContext.create(orm.em, next); }); ``` ## EntityManager - Querying Entities Fetch entities using various query methods with filtering, population, and ordering. ```typescript // Find single entity const author = await em.findOne(Author, { email: 'jon@wall.st' }); const authorById = await em.findOne(Author, 1); // by primary key // Find or throw const author2 = await em.findOneOrFail(Author, { email: 'missing@test.com' }); // throws NotFoundError if not found // Find multiple entities const authors = await em.find(Author, { age: { $gte: 18 } }); // Find all with options const books = await em.findAll(Book, { where: { price: { $gt: 20 } }, populate: ['author', 'tags'], orderBy: { title: 'ASC' }, limit: 10, offset: 0, }); // Pagination with total count const [results, total] = await em.findAndCount(Author, {}, { limit: 10, offset: 20, }); console.log(`Showing ${results.length} of ${total} authors`); // Complex filtering with operators const complexQuery = await em.find(Book, { $and: [ { price: { $gte: 10, $lte: 50 } }, { author: { age: { $gt: 20 } } }, { $or: [ { title: { $like: '%TypeScript%' } }, { tags: { name: 'Programming' } }, ]}, ], }); // Partial loading (select specific fields) const partial = await em.find(Author, {}, { fields: ['id', 'firstName', 'lastName'], }); // Using entity reference without loading const bookRef = em.getReference(Book, 1); author.favouriteBook = bookRef; // no query issued await em.flush(); // Cursor-based pagination const cursor = await em.findByCursor(Book, { first: 10, orderBy: { id: 'desc' }, }); console.log(cursor.items); // Book[] console.log(cursor.endCursor); // opaque string for next page // Next page const nextPage = await em.findByCursor(Book, { first: 10, after: cursor.endCursor, orderBy: { id: 'desc' }, }); ``` ## EntityManager - Updates and Deletions Update and remove entities using EntityManager methods with atomic operations support. ```typescript // Update via loaded entity (change tracking) const book = await em.findOneOrFail(Book, 1); book.title = 'Updated Title'; book.price = 39.99; await em.flush(); // Update via entity reference (without loading) const bookRef = em.getReference(Book, 1); bookRef.title = 'New Title'; bookRef.price = 29.99; await em.flush(); // Atomic update with raw SQL import { raw } from '@mikro-orm/core'; const book2 = em.getReference(Book, 2); book2.price = raw<number>(`price * 1.1`); // 10% increase await em.flush(); console.log(book2.price); // actual value after flush // Bulk update (native query, bypasses Unit of Work) const updated = await em.nativeUpdate(Book, { author: { age: { $lt: 18 } } }, { price: 0 } ); console.log(`Updated ${updated} books`); // Remove entity const toDelete = await em.findOneOrFail(Book, 3); em.remove(toDelete); await em.flush(); // Remove by reference (no loading) em.remove(em.getReference(Book, 4)); await em.flush(); // Bulk delete const deleted = await em.nativeDelete(Book, { price: { $lt: 5 } }); console.log(`Deleted ${deleted} books`); // Upsert - insert or update const upserted = await em.upsert(Author, { email: 'jane@example.com', // unique field for conflict detection firstName: 'Jane', lastName: 'Doe', age: 30, }); // INSERT ... ON CONFLICT (email) DO UPDATE ... // Upsert with options await em.upsert(Book, { id: 1, title: 'New Title', price: 25 }, { onConflictFields: ['id'], onConflictAction: 'merge', onConflictExcludeFields: ['createdAt'], }); ``` ## QueryBuilder Build complex SQL queries with type-safe QueryBuilder supporting joins, aggregates, and subqueries. ```typescript import { EntityManager, QueryOrder, LockMode } from '@mikro-orm/postgresql'; const em = orm.em as EntityManager; // Basic select const qb = em.createQueryBuilder(Book, 'b'); const books = await qb .select('*') .where({ price: { $gt: 10 } }) .orderBy({ title: 'ASC' }) .limit(10) .getResultList(); // Type-safe joins with alias tracking const authorsWithBooks = await em.createQueryBuilder(Author, 'a') .select(['a.firstName', 'a.lastName', 'b.title']) .leftJoin('a.books', 'b') // 'b' typed as Book .leftJoin('b.tags', 't') // 't' typed as BookTag .where({ 'b.price': { $gt: 20 } }) // type-checked .orderBy({ 'a.lastName': 'ASC', 't.name': 'DESC' }) .getResultList(); // Join and select (populate relations) const populatedAuthors = await em.createQueryBuilder(Author, 'a') .select('*') .leftJoinAndSelect('a.books', 'b') .leftJoinAndSelect('b.tags', 't') .where({ 't.name': 'TypeScript' }) .getResultList(); console.log(populatedAuthors[0].books[0].tags[0].name); // accessible // Aggregates with groupBy and having const stats = await em.createQueryBuilder(Book, 'b') .select(['b.author', sql`count(b.id)`.as('book_count')]) .leftJoin('b.author', 'a') .groupBy('b.author') .having({ book_count: { $gt: 5 } }) .execute(); // Update query await em.createQueryBuilder(Book) .update({ price: raw('price * 0.9') }) .where({ author: { firstName: 'Jon' } }) .execute(); // Delete query await em.createQueryBuilder(Book) .delete() .where({ price: { $lt: 5 } }) .execute(); // Subqueries const subQb = em.createQueryBuilder(Book, 'b') .select('b.author') .where({ price: { $gt: 100 } }); const richAuthors = await em.createQueryBuilder(Author, 'a') .select('*') .where({ id: { $in: subQb } }) .getResultList(); // Common Table Expressions (CTEs) const cteSub = em.createQueryBuilder(Author, 'a') .select(['a.id', 'a.firstName']) .where({ age: { $gte: 40 } }); const cteResult = await em.createQueryBuilder(Author) .with('senior_authors', cteSub) .select('*') .from('senior_authors', 'sa') .execute(); // Locking const locked = await em.createQueryBuilder(Book) .select('*') .where({ id: 1 }) .setLockMode(LockMode.PESSIMISTIC_WRITE) .getSingleResult(); ``` ## Entity Repository Create type-safe repositories for domain-specific query methods and encapsulated data access. ```typescript import { EntityRepository } from '@mikro-orm/postgresql'; // Custom repository export class AuthorRepository extends EntityRepository<Author> { async findByEmail(email: string): Promise<Author | null> { return this.findOne({ email }); } async findActiveWithBooks(): Promise<Author[]> { return this.find( { age: { $gte: 18 } }, { populate: ['books'], orderBy: { lastName: 'ASC' } } ); } async searchByName(query: string): Promise<Author[]> { return this.createQueryBuilder('a') .select('*') .where({ $or: [ { firstName: { $like: `%${query}%` } }, { lastName: { $like: `%${query}%` } }, ], }) .getResultList(); } } // Register repository in entity definition const AuthorSchema = defineEntity({ name: 'Author', repository: () => AuthorRepository, properties: { id: p.integer().primary(), // ... }, }); export class Author extends AuthorSchema.class {} AuthorSchema.setClass(Author); // Usage const authorRepo = em.getRepository(Author); // typed as AuthorRepository const author = await authorRepo.findByEmail('jon@example.com'); const active = await authorRepo.findActiveWithBooks(); // Generic base repository for shared methods export class BaseRepository<T extends object> extends EntityRepository<T> { async exists(where: FilterQuery<T>): Promise<boolean> { const count = await this.count(where); return count > 0; } async findOrCreate(where: FilterQuery<T>, data: RequiredEntityData<T>): Promise<T> { let entity = await this.findOne(where); if (!entity) { entity = this.create(data); await this.em.flush(); } return entity; } } // Register globally MikroORM.init({ entityRepository: () => BaseRepository, // ... }); ``` ## Transactions Handle database transactions with explicit demarcation, propagation control, and isolation levels. ```typescript import { MikroORM, Transactional, TransactionPropagation, IsolationLevel } from '@mikro-orm/core'; // Implicit transaction (flush wraps in transaction) const author = em.create(Author, { firstName: 'Jon', lastName: 'Snow', email: 'jon@wall.st' }); await em.flush(); // BEGIN, INSERT, COMMIT // Explicit transaction with callback await em.transactional(async (em) => { const author = em.create(Author, { firstName: 'Jane', lastName: 'Doe', email: 'jane@example.com' }); const book = em.create(Book, { title: 'My Book', price: 29.99, author }); // auto flush and commit on success, rollback on error }); // Manual transaction control const em2 = orm.em.fork(); await em2.begin(); try { em2.create(Author, { firstName: 'Bob', lastName: 'Smith', email: 'bob@example.com' }); await em2.commit(); } catch (e) { await em2.rollback(); throw e; } // Transaction with isolation level await em.transactional(async (em) => { const book = await em.findOneOrFail(Book, 1, { lockMode: LockMode.PESSIMISTIC_WRITE, }); book.price = book.price * 1.1; }, { isolationLevel: IsolationLevel.SERIALIZABLE }); // @Transactional decorator (for services) class BookService { constructor(private readonly em: EntityManager) {} @Transactional() async createWithAuthor(authorData: AuthorDTO, bookData: BookDTO) { const author = this.em.create(Author, authorData); const book = this.em.create(Book, { ...bookData, author }); // auto commit on success } @Transactional({ propagation: TransactionPropagation.REQUIRES_NEW }) async auditLog(action: string) { // runs in separate transaction this.em.create(AuditLog, { action, timestamp: new Date() }); } } // Nested transactions with savepoints await em.transactional(async (em1) => { const author = em1.create(Author, { firstName: 'Outer', lastName: 'Test', email: 'outer@test.com' }); try { await em1.transactional(async (em2) => { em2.create(Book, { title: 'Inner Book', price: 10, author }); throw new Error('Nested failure'); }); } catch (e) { // Inner transaction rolled back, outer continues console.log('Inner failed, outer continues'); } // author will be saved }); ``` ## Migrations Generate and run database migrations based on entity schema changes. ```typescript // Configuration in mikro-orm.config.ts import { defineConfig } from '@mikro-orm/postgresql'; import { Migrator } from '@mikro-orm/migrations'; export default defineConfig({ entities: [Author, Book], dbName: 'my-database', extensions: [Migrator], migrations: { tableName: 'mikro_orm_migrations', path: './dist/migrations', pathTs: './src/migrations', glob: '!(*.d).{js,ts}', transactional: true, allOrNothing: true, emit: 'ts', }, }); // CLI commands // npx mikro-orm migration:create # Create migration from schema diff // npx mikro-orm migration:create --blank # Create empty migration // npx mikro-orm migration:up # Run pending migrations // npx mikro-orm migration:down # Rollback one migration // npx mikro-orm migration:list # Show migration status // Migration class import { Migration } from '@mikro-orm/migrations'; export class Migration20240115120000 extends Migration { async up(): Promise<void> { this.addSql(` ALTER TABLE "author" ADD COLUMN "bio" TEXT NULL `); this.addSql(` CREATE INDEX "author_email_idx" ON "author" ("email") `); } async down(): Promise<void> { this.addSql('DROP INDEX "author_email_idx"'); this.addSql('ALTER TABLE "author" DROP COLUMN "bio"'); } } // Programmatic migration const orm = await MikroORM.init(); const migrator = orm.getMigrator(); await migrator.create(); // create new migration await migrator.up(); // run all pending await migrator.up({ to: 'Migration20240115' }); // up to specific await migrator.down(); // rollback one step await migrator.down({ to: 0 }); // rollback all const pending = await migrator.getPendingMigrations(); const executed = await migrator.getExecutedMigrations(); ``` ## Relationships and Collections Define and work with entity relationships including ManyToOne, OneToMany, ManyToMany, and polymorphic relations. ```typescript import { defineEntity, p, Collection } from '@mikro-orm/core'; // ManyToOne / OneToMany (bidirectional) const BookSchema = defineEntity({ name: 'Book', properties: { id: p.integer().primary(), title: p.string(), author: () => p.manyToOne(Author).inversedBy('books'), }, }); const AuthorSchema = defineEntity({ name: 'Author', properties: { id: p.integer().primary(), name: p.string(), books: () => p.oneToMany(Book).mappedBy('author').orphanRemoval(), }, }); // ManyToMany const TagSchema = defineEntity({ name: 'Tag', properties: { id: p.integer().primary(), name: p.string(), books: () => p.manyToMany(Book).mappedBy('tags'), }, }); const BookWithTagsSchema = defineEntity({ name: 'Book', properties: { id: p.integer().primary(), tags: () => p.manyToMany(Tag).inversedBy('books'), }, }); // Working with collections const author = await em.findOneOrFail(Author, 1, { populate: ['books'] }); // Check initialization console.log(author.books.isInitialized()); // true after populate // Add to collection const newBook = em.create(Book, { title: 'New Book' }); author.books.add(newBook); // Remove from collection author.books.remove(author.books[0]); // Collection operations console.log(author.books.count()); console.log(author.books.contains(newBook)); console.log(author.books.getItems()); // Book[] await em.flush(); // Polymorphic relation (one property referencing multiple entity types) const UserLikeSchema = defineEntity({ name: 'UserLike', properties: { id: p.number().primary(), // Can point to either Post or Comment likeable: () => p.manyToOne([Post, Comment]), }, }); // Usage const like = em.create(UserLike, { likeable: somePost }); // MikroORM sets likeable_type = 'post' automatically const loaded = await em.findOne(UserLike, 1, { populate: ['likeable'] }); if (loaded.likeable instanceof Post) { console.log('Liked a post:', loaded.likeable.title); } ``` ## Seeding Populate database with test or initial data using seeders and factories. ```typescript // Configuration import { defineConfig } from '@mikro-orm/postgresql'; import { SeedManager } from '@mikro-orm/seeder'; export default defineConfig({ extensions: [SeedManager], seeder: { path: './dist/seeders', pathTs: './src/seeders', defaultSeeder: 'DatabaseSeeder', }, }); // Factory definition import { Factory } from '@mikro-orm/seeder'; export class AuthorFactory extends Factory<Author> { model = Author; definition(): Partial<Author> { return { firstName: faker.person.firstName(), lastName: faker.person.lastName(), email: faker.internet.email(), age: faker.number.int({ min: 18, max: 80 }), }; } } // Seeder class import { Seeder } from '@mikro-orm/seeder'; export class DatabaseSeeder extends Seeder { async run(em: EntityManager): Promise<void> { // Using factory const authorFactory = new AuthorFactory(em); const authors = await authorFactory.create(10); // Create with specific values const admin = await authorFactory.createOne({ firstName: 'Admin', email: 'admin@example.com', }); // Manual creation em.create(Book, { title: 'Sample Book', price: 19.99, author: authors[0] }); // Call other seeders return this.call(em, [AuthorSeeder, BookSeeder]); } } // CLI commands // npx mikro-orm seeder:create AuthorSeeder // npx mikro-orm seeder:run // npx mikro-orm seeder:run --class=AuthorSeeder // Programmatic seeding const orm = await MikroORM.init(); const seeder = orm.getSeeder(); await seeder.seed(DatabaseSeeder); ``` ## Schema Generator Generate, update, and synchronize database schema from entity definitions. ```typescript // CLI commands // npx mikro-orm schema:create --dump # Show CREATE statements // npx mikro-orm schema:create --run # Execute CREATE statements // npx mikro-orm schema:update --dump # Show UPDATE statements // npx mikro-orm schema:update --run # Execute UPDATE statements // npx mikro-orm schema:drop --dump # Show DROP statements // npx mikro-orm schema:fresh --run # Drop and recreate // Programmatic usage const orm = await MikroORM.init(); const generator = orm.getSchemaGenerator(); // Generate SQL without executing const createSql = await generator.getCreateSchemaSQL(); const updateSql = await generator.getUpdateSchemaSQL(); const dropSql = await generator.getDropSchemaSQL(); console.log(createSql); // Execute schema changes await generator.createSchema(); await generator.updateSchema(); await generator.dropSchema(); // Fresh schema (drop and recreate) await generator.refreshDatabase(); // Configuration options export default defineConfig({ schemaGenerator: { disableForeignKeys: true, createForeignKeyConstraints: true, ignoreSchema: ['audit'], skipTables: ['legacy_users'], }, }); ``` ## Event Hooks and Lifecycle Handle entity lifecycle events using hooks and subscribers. ```typescript import { defineEntity, p, EventArgs, EventSubscriber, Subscriber } from '@mikro-orm/core'; // Property-level hooks in defineEntity const BookSchema = defineEntity({ name: 'Book', properties: { id: p.integer().primary(), title: p.string(), slug: p.string(), createdAt: p.datetime().onCreate(() => new Date()), updatedAt: p.datetime().onUpdate(() => new Date()), }, }); export class Book extends BookSchema.class {} BookSchema.setClass(Book); // Entity-level hooks via addHook BookSchema.addHook('beforeCreate', (args: EventArgs<Book>) => { args.entity.slug = args.entity.title.toLowerCase().replace(/\s+/g, '-'); }); BookSchema.addHook('beforeUpdate', (args: EventArgs<Book>) => { if (args.changeSet?.payload.title) { args.entity.slug = args.entity.title.toLowerCase().replace(/\s+/g, '-'); } }); // Global event subscriber @Subscriber() export class AuditSubscriber implements EventSubscriber { async beforeCreate(args: EventArgs<any>): Promise<void> { console.log('Creating entity:', args.entity.constructor.name); } async afterCreate(args: EventArgs<any>): Promise<void> { console.log('Created entity with id:', args.entity.id); } async beforeUpdate(args: EventArgs<any>): Promise<void> { const changes = args.changeSet?.payload; console.log('Updating:', changes); } async afterFlush(args: FlushEventArgs): Promise<void> { console.log('Flushed changes to database'); } } // Entity-specific subscriber @Subscriber() export class BookSubscriber implements EventSubscriber<Book> { getSubscribedEntities() { return [Book]; } async beforeCreate(args: EventArgs<Book>): Promise<void> { // only triggers for Book entities args.entity.slug = slugify(args.entity.title); } } // Register subscribers export default defineConfig({ subscribers: [AuditSubscriber, BookSubscriber], }); ``` ## Filtering and Soft Deletes Implement global entity filters for soft deletes and multi-tenancy. ```typescript import { defineEntity, p, Filter } from '@mikro-orm/core'; // Soft delete filter const BookSchema = defineEntity({ name: 'Book', properties: { id: p.integer().primary(), title: p.string(), deletedAt: p.datetime().nullable(), }, }); // Add filter to entity @Filter({ name: 'softDelete', cond: { deletedAt: null }, default: true }) export class Book extends BookSchema.class { async softDelete(em: EntityManager) { this.deletedAt = new Date(); await em.flush(); } } BookSchema.setClass(Book); // Define filters in configuration export default defineConfig({ filters: { softDelete: { cond: { deletedAt: null }, default: true }, }, }); // Usage - filter applied automatically const books = await em.find(Book, {}); // only non-deleted // Disable filter for specific query const allBooks = await em.find(Book, {}, { filters: { softDelete: false } }); // Include deleted const withDeleted = await em.find(Book, {}, { filters: false }); // Multi-tenant filter export default defineConfig({ filters: { tenant: { cond: (args) => ({ tenantId: args.tenantId }), default: true, }, }, }); // Set filter parameter em.setFilterParams('tenant', { tenantId: currentTenant.id }); ``` ## Summary MikroORM provides a comprehensive data access layer for Node.js applications with strong TypeScript integration. The library excels in complex domain modeling scenarios where the Data Mapper pattern, Unit of Work, and Identity Map provide clear separation between business logic and persistence concerns. Key features include automatic change tracking that batches database operations, type-safe query building with full IDE support, flexible entity definition through `defineEntity` or decorators, and robust migration and seeding tools. Integration patterns vary by framework: Express applications use `RequestContext` middleware for request-scoped EntityManagers, NestJS applications leverage the `@mikro-orm/nestjs` module for dependency injection, and serverless functions can use pre-compiled functions for edge runtime compatibility. The ORM supports advanced scenarios including polymorphic relations, table-per-type inheritance, composite primary keys, and multi-tenant architectures through entity filters. For high-performance use cases, streaming support, cursor-based pagination, and direct Kysely access provide fine-grained control over database operations.