Skip to content

In Search of the Fastest TypeScript ORM

Bar chart comparing SQL generation speed across TypeScript ORMs and query builders

Updated July 2026: re-run with the latest versions (TypeORM 1.0, MikroORM 7.1, UQL 0.9) and fairness improvements. All 7 entries now compile the PostgreSQL dialect, and MikroORM is measured via its parameterized toQuery() instead of the slower debug helper getFormattedQuery().

I kept seeing “just use Drizzle, it’s lightweight” and “ORMs are slow, use a query builder” repeated everywhere. So I decided to actually measure it.

The benchmark measures pure SQL generation speed: no database, no network, no connection pool. That isolates the overhead the ORM adds to every request. 7 entries, 8 query types, 3 runs averaged, on an Apple Silicon M4.


  • Environment: Node.js v24, Apple Silicon M4, 3 runs averaged.
  • Versions: Latest stable of every ORM/QB as of July 2026 (TypeORM 1.0.0, MikroORM 7.1.5, Sequelize 6.37.8, Drizzle 0.45.2, Knex 3.3.0, Kysely 0.29.2, UQL 0.9.4).
  • Fairness: Each ORM uses its most idiomatic API (QueryBuilder for TypeORM/MikroORM), which benefits them by skipping entity overhead.
  • What’s measured: Pure SQL string generation, with no database, no I/O, and no connection pool. This isolates ORM overhead only.
  • Why no Prisma? Prisma is not a pure TypeScript/JavaScript ORM; only its client is JS/TS. The query engine is a separate Rust binary that builds SQL, making it architecturally incomparable and not even testable this way.

Entry ops/sec vs winner
UQL 565K 1.00x
Knex 477K 0.84x
Sequelize 201K 0.36x
Kysely 194K 0.34x
MikroORM 111K 0.20x
TypeORM 41K 0.07x
Drizzle 13K 0.02x
Entry ops/sec vs winner
UQL 1,772K 1.00x
Kysely 812K 0.46x
Knex 711K 0.40x
TypeORM 289K 0.16x
Sequelize 242K 0.14x
MikroORM 222K 0.13x
Drizzle 79K 0.04x
Entry ops/sec vs winner
UQL 656K 1.00x
Knex 443K 0.68x
Kysely 339K 0.52x
Sequelize 334K 0.51x
TypeORM 269K 0.41x
MikroORM 265K 0.40x
Drizzle 37K 0.06x
Entry ops/sec vs winner
UQL 3,525K 1.00x
Sequelize 1,361K 0.39x
Kysely 1,269K 0.36x
Knex 1,090K 0.31x
TypeORM 521K 0.15x
MikroORM 261K 0.07x
Drizzle 209K 0.06x
Entry ops/sec vs winner
UQL 3,472K 1.00x
Sequelize 3,052K 0.88x
Kysely 1,551K 0.45x
Knex 1,129K 0.33x
TypeORM 585K 0.17x
MikroORM 566K 0.16x
Drizzle 229K 0.07x
Entry ops/sec vs winner
UQL 1,190K 1.00x
Knex 614K 0.52x
Kysely 412K 0.35x
Sequelize 403K 0.34x
TypeORM 284K 0.24x
MikroORM 73K 0.06x
Drizzle 60K 0.05x
Entry ops/sec vs winner
UQL 612K 1.00x
Knex 247K 0.40x
Kysely 218K 0.36x
TypeORM 162K 0.26x
Sequelize 155K 0.25x
Drizzle 35K 0.06x
MikroORM 27K 0.04x
Entry ops/sec vs winner
UQL 1,431K 1.00x
Sequelize 410K 0.29x
Knex 310K 0.22x
TypeORM 286K 0.20x
Kysely 206K 0.14x
Drizzle 74K 0.05x
MikroORM 72K 0.05x

Interactive charts →


1. The “lightweight” option is the slowest thing in the benchmark.

Drizzle, marketed as lightweight, is slower than Sequelize (a full ORM from 2014) in every single category. The functional expression-tree approach creates more intermediate objects than Sequelize’s simple string concatenation.

2. Standalone query builders can’t beat a well-designed ORM.

Knex and Kysely have zero entity/relation overhead; they’re just SQL string builders. Yet UQL, a full ORM with entities, relations, and migrations, is faster than both in all 8 categories, by 1.2x to 6.9x depending on the query. The conventional wisdom (“ORMs are slow”) doesn’t hold when the ORM pre-computes its metadata and avoids intermediate allocations.

3. MikroORM pays double.

MikroORM v7 uses Kysely internally as its SQL generator (it replaced Knex in v7), so every query goes through two compilation layers: MikroORM → Kysely → SQL string. In these tables, MikroORM averages about 4x slower than Kysely alone (from 1.3x on UPSERT to 8x on complex $or). That’s the cost of layering abstractions.


I got curious why the gap was so large, so I dug into the approach. Most ORMs figure out your schema at query time: “What table does User map to? What column is companyId? Is it nullable?” They answer these questions on every single query.

UQL answers them once at startup. Field-to-column mappings, table names, and relation paths are all pre-computed into lookup tables before the first query runs. At query time, generating SQL is just reading from a cache.

The other difference is allocation. When TypeORM builds a SELECT, it creates a QueryBuilder, then an expression tree, then walks the tree to produce SQL. UQL pushes SQL fragments directly into a string buffer, creating no intermediate objects and no garbage-collection pressure.


Database latency is 1-50ms and ORM overhead is microseconds, so for a low-traffic app the difference is noise. It starts to matter at scale. Take the WHERE + SORT + LIMIT query at 1,000 req/s: UQL (1,190K ops/s) spends roughly 1ms of CPU per second generating SQL, while MikroORM (73K ops/s) spends roughly 14ms, about 16x more CPU for the same queries before a single byte hits the network. In serverless, where you pay per ms of CPU, that shows up on the bill; in containers, it shows up as horizontal scaling cost.


Full disclosure: I’m the author of UQL. That’s exactly why I built the benchmark as an independent repo anyone can audit and reproduce.

The full benchmark is open source and needs no database; it runs in seconds on Node.js. If your results differ, open an issue. The latest results always live on the benchmark page, and there’s a broader feature-by-feature comparison on the comparison page.

Repo: ts-orm-benchmark