The JSON-native TypeScript ORM
Every key checked, three levels deep
Section titled “Every key checked, three levels deep”A query is a plain object literal: no builder to call, no operator to import, and every key checked to the
leaf. All three errors below are compile errors: a misspelled key, the same mistake three levels into the
query, and an operator the column’s type rules out. None of them is written into this page; they are what
tsc reports against the published uql-orm.
import { pool } from './uql.config.js';import { User } from './entities.js';
await pool.findMany(User, { // the entity class types every key $select: { id: true, emial: true }, // same three levels deep $populate: { posts: { $select: { titel: true } } }, // and operators against the column type $where: { loginCount: { $like: 3 } },});import { Entity, Field, Id, ManyToOne, OneToMany } from 'uql-orm';
@Entity()export class Post { @Id({ type: Number }) id?: number; @Field({ type: String }) title?: string; @ManyToOne({ entity: () => User }) author?: User;}
@Entity()export class User { @Id({ type: 'uuid' }) id?: string; @Field({ type: String }) email?: string; @Field({ type: Number }) loginCount?: number; @OneToMany({ entity: () => Post, mappedBy: (post) => post.author }) posts?: Post[];}import { PgQuerierPool } from 'uql-orm/postgres';import { User } from './entities.js';
export const pool = new PgQuerierPool({ connectionString: process.env.DATABASE_URL,});
export default { pool, entities: [User] };None of this needs a code generation step. Entities are plain classes using the standard TC39 decorators, so there is no schema file to keep in sync, no client to regenerate, and no compiler flag to enable. The same checking reaches into JSON/JSONB dot-paths, down to a key inside a stored document. See Entities.
Same query can travel between browser, edge, and backends
Section titled “Same query can travel between browser, edge, and backends”Queries are serializable, so the browser can build and send one with the same type-safety. Four files, one call each: the browser call, the entity, the pool, and the endpoint that exposes it:
import { HttpQuerier } from 'uql-orm/browser';
const http = new HttpQuerier('/api');
const { data } = await http.findMany(User, { $select: { id: true, email: true }, $where: { email: { $endsWith: '@uql-orm.dev' } },});import { v7 as uuidv7 } from 'uuid';import { Entity, Id, Field } from 'uql-orm';
@Entity()export class User { @Id({ type: 'uuid', onInsert: uuidv7 }) id?: string;
@Field({ type: String, unique: true }) email?: string;}import { PgQuerierPool } from 'uql-orm/postgres';
export const pool = new PgQuerierPool({ connectionString: process.env.DATABASE_URL,});import { createFetchHandler } from 'uql-orm/http';
export const handler = createFetchHandler({ pool, include: [User] });That handler mounts on Hono, Elysia, Next.js, Express, Bun, Deno, or Workers, and carries transactions and authorization hooks across. More on the HTTP transport and the browser client.
The rest, briefly
Section titled “The rest, briefly”One package, zero runtime dependencies and every dialect included, yet uql-orm/postgres is about 27 kB gzipped. The same code runs on
PostgreSQL, PGlite, CockroachDB, MySQL, MariaDB, MSSQL, SQLite, Turso, libSQL, Neon, Cloudflare D1, Bun’s native SQL
and MongoDB, under Node 24+, Bun, Deno, Workers, Lambda and Vercel, and
the browser. It is ESM only, which rules out CommonJS projects.
Relations never hit N+1: the whole graph is one statement, however many rows come back, loaded eagerly so serializing the result touches nothing.
Migrations are generated from your entities and reviewed as SQL in the pull request, with
drift:check to catch a database that stopped matching. raw() fits
anywhere a value does when you want the SQL yourself. Semantic and vector search,
tenant filters that fail closed, soft delete with restore and
streaming are all included.
On a full PostgreSQL round trip UQL adds less over hand-written driver code than any other ORM we measured, on Bun, Node and Deno alike. Full benchmark, feature-by-feature comparison, as well as type-safety comparison.