Skip to content
UQL

SQLite

UQL speaks SQLite through three drivers. All of them produce identical SQL and identical results; they differ only in what you have to install and how fast they read.

Pool Driver Install
NodeSqliteQuerierPool Node’s built-in node:sqlite nothing
Sqlite3QuerierPool better-sqlite3, or bun:sqlite under Bun npm i better-sqlite3 (Bun needs nothing)
TursoQuerierPool / LibsqlQuerierPool libSQL / Turso over the wire see Turso

If SQLite here is standing in for a Postgres you run in production, PGlite needs no server either and is actually Postgres.

NodeSqliteQuerierPool uses the SQLite that ships inside Node, so there is no native module to build and nothing to install:

import { NodeSqliteQuerierPool } from 'uql-orm/sqlite';
export const pool = new NodeSqliteQuerierPool('app.db');

That matters most where a native build is awkward: slim container images, CI without a toolchain, and anywhere node-gyp is unwelcome. UQL requires Node 24, which is well past the 22.13 where node:sqlite became usable, so there is no version to check.

Loadable extensions work too, which is what vector search needs, since SQLite ships no vector functions of its own:

import { getLoadablePath } from 'sqlite-vec';
const pool = new NodeSqliteQuerierPool('app.db', {
extensions: [getLoadablePath()],
});

$text matches an FTS5 virtual table, which migrations do not (automatically) create: make it yourself and declare an entity over it, then $text and $sort: { $text } (ranked by BM25) work as on any engine. The same goes for libSQL, Turso and D1, which all have FTS5. See Full-text search.

better-sqlite3 is faster on reads. Measured on 20k in-memory rows, node:sqlite was about 20% quicker on inserts but 1.3x slower on point reads and 1.4x slower on 100-row reads. Reads dominate most workloads, so better-sqlite3 stays the recommendation when throughput matters and installing a native module is not a problem.

node:sqlite is also still a release candidate in Node’s own stability index, while better-sqlite3 is long settled.

import { Sqlite3QuerierPool } from 'uql-orm/sqlite';
export const pool = new Sqlite3QuerierPool('app.db');

Under Bun this same pool uses bun:sqlite automatically, so Bun projects install nothing either.

A populated to-many is aggregated with json_group_array, whose ORDER BY needs SQLite 3.44. Turso lacks it, so its rows keep the subquery’s order.

A populated float keeps 15 significant digits on SQLite 3.51 and libSQL, 17 on 3.53.