Zero dependencies: what we deleted


npm i uql-orm installs one package. No dependencies, every dialect included.
A month ago it pulled in four mandatory packages, roughly 4 MB unpacked; now it is 992 KB.
One release, four dependencies gone
Section titled “One release, four dependencies gone”0.21.0 did it in one pass: ~4.0 MB down to 992 KB, cold start from 16.9 ms to 8.9 ms. Four packages, one job each, none worth a dependency of its own.
tslib existed so TypeScript’s helper functions didn’t repeat across files. "importHelpers": false costs a few hundred bytes per file instead of a dependency edge.
sqlstring escaped MySQL and MariaDB literals, badly: a Uint8Array came out as `0` = 255 instead of X'ff00', a plain object as '[object Object]'. No crash, no warning, just wrong output. Dialect.escape handles all three SQL dialects itself now, byte-for-byte identical across 29 value shapes, and those two cases throw instead of writing garbage.
reflect-metadata (264 KB) earned its keep with one call, Reflect.getMetadata('design:type', ...), so @Field() could guess a column’s type from the property. Optional as of this release, gone two releases later.
jiti (1.8 MB), a full TypeScript transpiler, existed to read one file: uql.config.ts. That’s a lot of compiler to hire for one sticky note. Gone too; the config now needs a runtime that already speaks TypeScript (bun, or node --import tsx).
Then what was never code: 369 sourcemaps nobody opened, and a 108 KB CHANGELOG, shipped in every install. files in package.json is ["dist", "README.md"] now.
Then reflection went too
Section titled “Then reflection went too”Two releases later, reflect-metadata stopped being optional and started being pointless. 0.23.0 moved decorators to the TC39 standard spec: @Field() and @Id() require an explicit type now, checked against the property, so there’s nothing left to reflect. The full story, including what it cost, is in Standard decorators: props & cons.
What one npm i puts on disk
Section titled “What one npm i puts on disk”Install one package into an empty project and count the bytes yourself:
npm i --omit=dev uql-ormfind node_modules -type f -exec cat {} + | wc -cUnpacked bytes on disk, not the 288 kB you download. Tarballs are compressed, but a cold start loads the unpacked files.
| Package | Installed | Files |
|---|---|---|
uql-orm 0.24.1 |
1.0 MB | 384 |
@mikro-orm/ 7.1.9 |
4.7 MB | 1,153 |
drizzle-orm 0.45.2 |
9.9 MB | 2,667 |
sequelize 6.37.8 |
15.0 MB | 2,708 |
typeorm 1.1.0 |
22.5 MB | 3,663 |
@prisma/ 7.9.1 |
75.0 MB | 94 |
One caveat: 93% of @prisma/client is 70 MB of Rust query compilers cross-compiled to WebAssembly, one per engine it supports. UQL ships every dialect too, so per dialect is the fairer view: 4.9 MB for Prisma’s Postgres compiler alone, against 19.9 kB gzipped for UQL’s entire Postgres entry point.
No row includes a driver. Add pg to any of them and they all grow the same amount. This table is the floor.
What the small number doesn’t cover
Section titled “What the small number doesn’t cover”UQL is small; your driver may not be. pg, mysql2, mariadb, mongodb and better-sqlite3 carry their own weight, and the last needs a native build. UQL’s promise is narrower than “small”: it adds nothing on top of whichever one you picked.
Every entry point has a gzip budget checked on every build, added after 0.13.0 let node:async_hooks leak into browser bundles and had to be pulled.
Get started
Section titled “Get started”npm i uql-ormIf your install contains something that shouldn’t be there, open an issue.
UQL 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.