UnqliteStore — embedded serverless key-value engine
Read this page in the documentation
UnqliteStore — embedded serverless key-value engine Overview UnQLite is an embedded, in-process, serverless database engine with a simple key-value store layer (it also has a document store, which is not modelled here). The database is opened directly against a filesystem path and lives inside your process. UnqliteStore maps a common KV surface (put/get/del) onto UnQLite's native store/fetch/delete calls, plus record iteration. It has no SQL-shaped surface (query(sql), identifier escaping, DDL). Because none of that fits the SQL-shaped Dialect interface, UnqliteStore implements the minimal NoSqlStore marker interface (src/nosql/store.ts) — connection lifecycle plus a getClient() escape hatch — and exposes UnQLite's KV surface directly. Identity: Property | Value | --------- | ------------ | name | 'unqlite' | library | 'unqlite' | The store is built on the unqlite npm driver, which is callback-based. The store wraps each callback call into a Promise via a private promisify helper. Lazy loading — not a hard dependency The unqlite driver is not a hard dependency of this package. It is loaded lazily via require('unqlite') inside connect(), rather than a top-level import. Importing this module therefore does not require the driver to be installed — it is only needed when an UnQLite store is actually connected without an injected client. On load, connect() calls new UnQLite(path). Injected client UnqliteStoreOptions accepts a pre-built client (any object matching UnqliteClientLike). When provided, connect() uses it directly and skips require('unqlite') entirely. This is how the test suite injects a callback-based mock client (no driver, no filesystem), and how callers can supply a custom-configured client. Connection Build a store from connection options and call connect(): Connection options (UnqliteStoreOptions): Option | Type | Purpose | -------- | -------------------- | ------------------------------------------------------------------------------------------- | path | string | Filesystem path for the UnQLite database file. Defaults to './data/unqlite.db'. | client | UnqliteClientLike | A pre-built client to use directly (mock or custom). When set, the driver is not required. | When no client is provided, connect() calls require('unqlite') and opens new UnQLite(path). Injected-client form Supply your own client (or a mock) to bypass driver-based construction: Methods Every operation resolves the client via a private guard: using any method before connect() (or after disconnect()) throws a ConnectionError. Driver failures are wrapped in a DatabaseError (preserving the original error) with an UnQLite <action> failed: … message. Lifecycle Method | Signature | Behavior | ------------ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------- | connect | connect(): Promise<void> | Uses an injected client if provided, otherwise lazy-requires unqlite and opens new UnQLite(path). Idempotent when already connected. Wraps failures in ConnectionError. | disconnect | disconnect(): Promise<void> | Best-effort client.close(cb) (if present, promisified), then clears connection state. | isConnected| isConnected(): boolean | true only when connected and a client is present. | getClient | getClient(): UnqliteClientLike | Returns the underlying client for operations not wrapped here. Throws ConnectionError if not connected. | Key-value operations Method | Signature | Behavior | ------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | put | put(key: string, value: unknown): Promise<void> | Writes (or overwrites) the value at key. Maps onto client.store(key, value, cb). | get | get(key: string): Promise<unknown> | Reads the value at key. Maps onto client.fetch(key, cb). | del | del(key: string): Promise<void> | Deletes the value at key. Maps onto client.delete(key, cb). | batch | batch(ops: UnqliteBatchOp[]): Promise<void> | Applies a list of put/del operations sequentially (awaiting each), routing put to put() and del to del(). | list | list(): Promise<string[]> | Lists all keys by iterating records with UnQLite's each cursor, collecting each key as a string. (No prefix argument.) | UnqliteBatchOp is the entry type for a batch(): Example Verification status Unit / mock-verified only. The tests in tests/nosql/unqlite.test.ts are fully mock-driven: a callback-based fake client whose methods are jest spies (each invoking its callback with canned results) is injected via UnqliteStoreOptions.client. The real unqlite driver is not installed, and there is no filesystem and no network in the test run — because a client is injected, connect() never reaches require('unqlite'). What this proves: Each method maps to the correct callback-style client call (store, fetch, delete, each) with the expected key/value arguments and a callback function. batch() maps put/del ops onto store/delete; list() collects keys via a single each() pass. Lifecycle: connecting via an injected client without loading the driver, disconnect() calling close, idempotent re-connect, and ConnectionError before connect() / via getClient() when not connected. Error handling: put/get callback errors wrapped in DatabaseError. What this does not prove: live execution against a real UnQLite database on disk. The callback/result shapes are verified against the driver's documented contract, but end-to-end execution against the native engine has not been exercised here. Related reading All data stores — the full catalogue, grouped by purpose Database types — where this sits among the 22 categories