> Every UQL docs page, as Markdown: https://uql-orm.dev/llms.txt
> The same docs over MCP: https://uql-orm.dev/mcp
> Before writing UQL code, read the skill: https://uql-orm.dev/.well-known/agent-skills/uql-orm/SKILL.md

# In search of the fastest TypeScript ORM

> A SQL-generation speed benchmark across six TypeScript ORMs: Drizzle, Knex, MikroORM, Sequelize, TypeORM and UQL. Reproducible and open source.

Source: https://uql-orm.dev/blog/in-search-of-the-fastest-typescript-orm

> **Superseded**
>
> This post measures SQL generation alone, with no database involved. That benchmark is retired: generation turned out to be well under 0.5% of a real request, so it said little about how ORMs compare. The [benchmark page](https://uql-orm.dev/benchmark.md) and [What does an ORM really cost you?](https://uql-orm.dev/blog/what-orms-really-cost.md) measure a full PostgreSQL round trip instead. The numbers below are kept as they were, for the record.

I kept reading *“just use Drizzle, it’s lightweight”* and *“ORMs are slow, use a query builder”*. So I measured it.

The benchmark measures **SQL generation speed** alone: no database, no network, no connection pool, so what is left is the overhead the ORM adds to every request. Six entries, eight query types.

---

## Methodology

- **Environment:** Node.js v24.18.1, Apple M4 Pro, 3 runs averaged.
- **Versions:** Latest stable of every entry as of August 2026 (TypeORM 1.1.0, MikroORM 7.1.9, Sequelize 6.37.8, Drizzle 0.45.2, Knex 3.3.0, UQL 0.24.0).
- **Fairness:** Each ORM uses its most idiomatic API (QueryBuilder for TypeORM/MikroORM), which benefits them by skipping entity overhead.
- **Why no Prisma?** Prisma’s query compiler is Rust compiled to WebAssembly rather than pure JS/TS, and it exposes no public SQL-compilation API, so it cannot be measured this way.

---

## The results

### INSERT: 10 rows in batch

| Entry | ops/sec | vs winner |
| - | - | - |
| UQL | 698K | 1.00x |
| Knex | 463K | 0.66x |
| Sequelize | 196K | 0.28x |
| MikroORM | 111K | 0.16x |
| TypeORM | 42K | 0.06x |
| Drizzle | 12K | 0.02x |

### UPDATE: SET + WHERE

| Entry | ops/sec | vs winner |
| - | - | - |
| UQL | 2,161K | 1.00x |
| Knex | 695K | 0.32x |
| TypeORM | 282K | 0.13x |
| Sequelize | 236K | 0.11x |
| MikroORM | 218K | 0.10x |
| Drizzle | 79K | 0.04x |

### UPSERT: ON CONFLICT by id

| Entry | ops/sec | vs winner |
| - | - | - |
| UQL | 691K | 1.00x |
| Knex | 433K | 0.63x |
| Sequelize | 327K | 0.47x |
| TypeORM | 261K | 0.38x |
| MikroORM | 260K | 0.38x |
| Drizzle | 36K | 0.05x |

### DELETE: simple WHERE

| Entry | ops/sec | vs winner |
| - | - | - |
| UQL | 3,996K | 1.00x |
| Sequelize | 1,361K | 0.34x |
| Knex | 1,084K | 0.27x |
| TypeORM | 507K | 0.13x |
| MikroORM | 263K | 0.07x |
| Drizzle | 207K | 0.05x |

### SELECT: 1 field

| Entry | ops/sec | vs winner |
| - | - | - |
| UQL | 4,675K | 1.00x |
| Sequelize | 3,084K | 0.66x |
| Knex | 1,092K | 0.23x |
| TypeORM | 591K | 0.13x |
| MikroORM | 565K | 0.12x |
| Drizzle | 229K | 0.05x |

### SELECT: WHERE + SORT + LIMIT

| Entry | ops/sec | vs winner |
| - | - | - |
| UQL | 1,365K | 1.00x |
| Knex | 614K | 0.45x |
| Sequelize | 381K | 0.28x |
| TypeORM | 276K | 0.20x |
| MikroORM | 73K | 0.05x |
| Drizzle | 59K | 0.04x |

### SELECT: complex $or + operators

| Entry | ops/sec | vs winner |
| - | - | - |
| UQL | 741K | 1.00x |
| Knex | 243K | 0.33x |
| TypeORM | 158K | 0.21x |
| Sequelize | 150K | 0.20x |
| Drizzle | 34K | 0.05x |
| MikroORM | 28K | 0.04x |

### AGGREGATE: GROUP BY + COUNT + HAVING

| Entry | ops/sec | vs winner |
| - | - | - |
| UQL | 1,482K | 1.00x |
| Sequelize | 416K | 0.28x |
| Knex | 304K | 0.21x |
| TypeORM | 277K | 0.19x |
| Drizzle | 74K | 0.05x |
| MikroORM | 69K | 0.05x |

**[Interactive charts](https://rogerpadilla.github.io/ts-orm-benchmark/chart.html)**

---

## Two things that surprised me

**1. The “lightweight” option comes last in six of eight queries.**

Drizzle, marketed as lightweight, is slower than Sequelize (a full ORM from 2014) in every category. Its expression trees create more intermediate objects than Sequelize’s string concatenation.

**2. A standalone query builder can’t beat a well-designed ORM.**

Knex has no entities or relations to deal with; it only builds SQL strings. Yet UQL, a full ORM with entities, relations and migrations, is faster in all eight categories, from 1.5x on batch INSERT to 4.9x on aggregates. “ORMs are slow” stops being true when the ORM precomputes its metadata and avoids intermediate allocations.

---

## How UQL gets there

I wanted to know why the gap was so large. Most ORMs work 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 query.

UQL answers them once, at startup. Field-to-column mappings, table names and relation paths go into lookup tables before the first query runs, so generating SQL is 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 straight into a string buffer, with no intermediate objects for the garbage collector.

---

## When this matters in production

Database latency is 1-50ms and ORM overhead is microseconds, so for a low-traffic app the difference is noise. At scale it starts to count. Take the WHERE + SORT + LIMIT query at 1,000 req/s: UQL (1,365K ops/s) spends under 1ms of CPU per second generating SQL, MikroORM (73K ops/s) roughly 14ms, about 19x more before a byte reaches the network. On serverless, where you pay per millisecond of CPU, that shows up on the bill; in containers, as more instances.

---

## Reproduce it

I wrote UQL, which is why the benchmark lives in its own repo that anyone can audit and rerun.

This SQL-generation benchmark is no longer maintained (see the note above). The current one, with its repro steps, is on the [benchmark page](https://uql-orm.dev/benchmark.md), and the [comparison page](https://uql-orm.dev/comparison.md) goes feature by feature.

---

---

*[UQL](https://uql-orm.dev/index.md) is a JSON-native TypeScript ORM for Node.js and Bun. Supports PostgreSQL, PGlite, MySQL, MariaDB, MSSQL, SQLite, CockroachDB, Turso, Neon, Cloudflare D1 and MongoDB. Queries are plain JSON, typed to the leaf.*
