Getting Started
UQL is the smartest ORM for TypeScript. It is engineered to be fast, safe, and universally compatible.
- Runs Everywhere: Node.js, Bun, Deno, Cloudflare Workers, Electron, React Native, and even the Browser.
- Unified API: A consistent, expressive query interface for PostgreSQL, MySQL, MariaDB, SQLite, LibSQL, Neon, Cloudflare D1, and MongoDB (inspired by its glorious syntax).
const companyUsers = await userRepository.findMany({ $select: { email: true, profile: ['picture'] }, $where: { email: { $endsWith: '@example.com' } }, $sort: { createdAt: -1 }, $limit: 100,});
Why UQL?
Section titled “Why UQL?”See this article in medium.com.
Features
Section titled “Features”- Type-safe and Context-aware queries: squeeze the powers of
TypeScriptso it auto-completes and validates, the appropriate operators on any level of the queries, including the relations and their fields. - Serializable queries: its syntax can be
100%validJSONallowing the queries to be transported across platforms with ease. - Unified API across Databases: same query is transparently transformed according to the configured database.
- FP + OOP: Combines the best elements of
FP(Functional Programming) andOOP(Object Oriented Programming). - Declarative and imperative
transactionsfor flexibility, andconnection poolingfor scalability. - Transparent support for inheritance between entities for reusability and consistency.
- Modern Pure ESM:
ESMis natively supported by Node.js 16 and later. - High performance: the generated queries are fast, safe, and human-readable.
- Supports the Data Mapper pattern for maintainability.
- soft-delete, virtual fields, repositories.
- Automatic handing of
json,jsonbandvectorfields.
1. Install
Section titled “1. Install”-
Install the core package:
Terminal window npm install @uql/core --save -
Install the driver for your database:
| Database | Driver | UQL Adapter |
|---|---|---|
PostgreSQL | pg | @uql/core/postgres |
SQLite | better-sqlite3 | @uql/core/sqlite |
MariaDB | mariadb | @uql/core/maria |
MongoDB | mongodb | @uql/core/mongo |
MySQL | mysql2 | @uql/core/mysql |
For example, for Postgres:
npm install pg --save(UQL adapters are now included in @uql/core)
-
Additionally, your
tsconfig.jsonmay need the following flags:{"compilerOptions": {"target": "es2022","experimentalDecorators": true,"emitDecoratorMetadata": true}}
2. Define the entities
Section titled “2. Define the entities”Take any class and annotate it with the decorators from @uql/core.
import { v7 as uuidv7 } from 'uuid';import { Id, Field, Entity } from '@uql/core';
@Entity()export class User { @Id({ onInsert: () => uuidv7() }) id?: string;
@Field() name?: string;
@Field({ updatable: false }) email?: string;
@Field({ eager: false }) password?: string;}
3. Setup a querier-pool
Section titled “3. Setup a querier-pool”A querier-pool can be set in any of the bootstrap files of your app (e.g. in the server.ts).
import { PgQuerierPool } from '@uql/core/postgres';import { SnakeCaseNamingStrategy } from '@uql/core';
export const pool = new PgQuerierPool( { host: 'localhost', user: 'theUser', password: 'thePassword', database: 'theDatabase', }, { logger: console.debug, namingStrategy: new SnakeCaseNamingStrategy(), },);
4. Manipulate the data
Section titled “4. Manipulate the data”UQL provides multiple ways to interact with your data, from low-level Queriers to high-level Repositories.
Using Repositories (Recommended)
Section titled “Using Repositories (Recommended)”import { GenericRepository } from '@uql/core';import { User } from './shared/models/index.js';import { pool } from './shared/orm.js';
// Get a querier from the poolconst querier = await pool.getQuerier();
try { const userRepository = new GenericRepository(User, querier);
const users = await userRepository.findMany({ $select: { id: true, name: true }, $where: { email: { $iincludes: '@example.com' } }, $sort: { createdAt: -1 }, $limit: 100 });} finally { // Always release the querier to the pool await querier.release();}Using Transactions
Section titled “Using Transactions”import { pool } from './shared/orm.js';import { User, Profile } from './shared/models/index.js';
const result = await pool.transaction(async (querier) => { const user = await querier.findOne(User, { $where: { email: '...' } }); const profileId = await querier.insertOne(Profile, { userId: user.id, ... }); return { userId: user.id, profileId };});// Connection is automatically released after transaction