Skip to content

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

 

See this article in medium.com.

 

  • Type-safe and Context-aware queries: squeeze the powers of TypeScript so 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% valid JSON allowing 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) and OOP (Object Oriented Programming).
  • Declarative and imperative transactions for flexibility, and connection pooling for scalability.
  • Transparent support for inheritance between entities for reusability and consistency.
  • Modern Pure ESM: ESM is 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, jsonb and vector fields.

 

  1. Install the core package:

    Terminal window
    npm install @uql/core --save
  2. Install the driver for your database:

DatabaseDriverUQL Adapter
PostgreSQLpg@uql/core/postgres
SQLitebetter-sqlite3@uql/core/sqlite
MariaDBmariadb@uql/core/maria
MongoDBmongodb@uql/core/mongo
MySQLmysql2@uql/core/mysql

For example, for Postgres:

Terminal window
npm install pg --save

(UQL adapters are now included in @uql/core)

  1. Additionally, your tsconfig.json may need the following flags:

    {
    "compilerOptions": {
    "target": "es2022",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
    }
    }

 


 

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

 

A querier-pool can be set in any of the bootstrap files of your app (e.g. in the server.ts).

./shared/orm.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(),
},
);

 

UQL provides multiple ways to interact with your data, from low-level Queriers to high-level Repositories.

import { GenericRepository } from '@uql/core';
import { User } from './shared/models/index.js';
import { pool } from './shared/orm.js';
// Get a querier from the pool
const 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();
}
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