Skip to content
NewFrom Mongoose to SQL6 min read

JSON-native TypeScript ORM

UQL queries SQL databases and MongoDB with plain, type-safe JSON. No codegen, no query builder.

Using a coding agent? Copy the setup prompt

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 } },
});

A query is a plain object: no builder to chain, no operators to import. The compiler checks every key at any depth, and the operators ($in, $gte, $or, …) are MongoDB’s. The red lines in the editor above are real tsc errors against the published types: a misspelled field, the same typo inside a populated relation, and an operator the column’s type does not allow.

Entities are plain classes with standard decorators, so there is no schema file and no client to regenerate. Checking reaches into JSON dot-paths too. See Entities.

A query is data, so the browser can send it over HTTP, typed the same as on the server:

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' } },
});

The handler runs each write in a transaction, takes authorization hooks, and mounts on Hono, Elysia, Next.js, Express, Bun or Workers. See the HTTP transport and the browser client.

Compare it feature by feature and on type safety.