A TypeScript ORM built for raw performance. Stage 3 decorators,
JIT-compiled hydrators, zero N+1 — and no reflect-metadata.
Mirror ORM is a unipurpose tool. It maps entities. It does it exceptionally well.
buildHydrator() uses new Function() to generate a monomorphic function per entity at startup. V8 applies Inline Caches to every field access — 6× faster than Drizzle and 8× faster than TypeORM in the same benchmark.
Uses the TC39 Stage 3 proposal natively in TypeScript 5.x. No reflect-metadata. No legacy flags.
findAll and findById use stable named queries, letting the pg driver skip parse & plan on every subsequent call.
Entities hydrated from the DB carry a column snapshot. save() only emits SET for changed columns — no unnecessary writes.
AsyncLocalStorage propagates the transaction context automatically — any repository created inside connection.transaction() uses it without explicit wiring.
Built-in hooks for before/after persist events. Timestamps, soft delete, and upsert are first-class features — not afterthoughts.
Modern identifier strategies built in. Choose the right one for your ordering and collision requirements.
Class-based, decorator-driven. If you've used TypeORM, it feels instantly familiar — but faster.
import { Entity, Column, PrimaryColumn } from 'mirror-orm'; @Entity() export class User { @PrimaryColumn() id: string; @Column() name: string; @Column({ type: 'datetime' }) createdAt: Date; @Column({ select: false }) passwordHash: string; }
const repo = connection.getRepository(User); // find with filters const users = await repo.find({ where: { name: Like('%alice%') }, order: { createdAt: 'DESC' }, limit: 10, }); // eager relations const post = await repo.findOne({ where: { id }, relations: ['author', 'tags'], });
Queries emit SELECT "col1", "col2" — never SELECT *. PostgreSQL skips unrequested columns. Use select: false to permanently hide sensitive fields like password hashes.
Like, In, Between, IsNull, Not — plus OR-group composition for expressive WHERE clauses without a query builder DSL.
saveMany() issues a single multi-row INSERT. removeMany() uses WHERE pk = ANY($1) — O(1) SQL regardless of array size.
Declare type: 'datetime' and get a real Date object back. bigint, boolean, number, iso — woven directly into the JIT hydrator, zero runtime branching per row.
Opt-in loading, no N+1, no surprises. Mirror resolves all relation types with a single batch query each.
Generates a LEFT JOIN in find({ relations }). Related columns are aliased with a mirror__prop__ prefix to prevent name collisions across multiple joins.
After the main query, a single batch SELECT … WHERE fk = ANY($1) fetches all children for all parent IDs at once. Guaranteed zero N+1 queries.
Owner side resolved via FK presence on the entity — uses LEFT JOIN (same path as ManyToOne). Inverse side uses a batch query returning T | null.
Batch INNER JOIN through the join table with owner FK aliased internally for grouping. Returns T[]. No hidden junction queries.
Mirror's repository layer is split by responsibility — nothing ends up in the wrong place.
Column maps, select clauses, JIT hydrators, and prepared statement objects — all built once at getRepository() time. Never on the hot path.
Builds SQL strings. Each method returns { sql, params } — no side effects, no mutation. Pure functions, easy to test.
A thin orchestration layer. Calls SqlAssembler for SQL, executes via IQueryRunner, hydrates via RepositoryState. Zero SQL strings inline.
Mirror ORM is production-ready and actively maintained. Install it, try it, and open an issue — contributions are welcome.