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 |
|---|---|---|
Node |
Node’s built-in node:sqlite |
nothing |
Sqlite3Querier |
better-sqlite3, or bun:sqlite under Bun |
npm i better-sqlite3 (Bun needs nothing) |
Turso / Libsql |
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.
No dependency at all
Section titled “No dependency at all”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()],});Full-text search
Section titled “Full-text search”$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.
When to prefer better-sqlite3
Section titled “When to prefer better-sqlite3”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.
Populated relations
Section titled “Populated relations”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.