Part 8 · 1 chapters · ~8 min
Capstone: Build Your Own ORM
Building a small TypeScript ORM: a schema definition with inferred types, a query builder producing parameterised SQL, an identity map with unit of work and dirty tracking, batched relation loading that makes N+1 impossible, a migration engine with diffing, and transactions with pooling.
12
The build
code
mini-orm/
src/schema.ts table('accounts', { id: text().primaryKey(), balanceKobo: bigint() }) → InferRow<typeof accounts>
src/query.ts select().from(t).where(eq(t.ownerId, x)) → { sql, params }; Postgres dialect
src/session.ts identity map (Map<table:id, entity>), snapshots, flush(): diff → ordered statements in one tx
src/loader.ts session.load(Account, id) batched per tick → WHERE id = ANY($1)
src/migrate.ts read information_schema, diff, emit SQL; refuse DROP COLUMN without --allow-destructive
test/ exact-SQL snapshots; a flush-order test with FK constraints; an N+1 test that counts queries
stretch: optimistic locking with a version column; soft deletes; a raw-SQL escape hatch that stays parameterisedBUILD AN ORM
the capstone, in six components
swipe the figure sideways, or tap expand for full screen
1/5
schema with inference
Define tables in TypeScript (like Drizzle) so row types are inferred from the definition.
definitions → inferred typesno codegen