LmdbStore — embedded memory-mapped key-value store

Read this page in the documentation

LmdbStore — embedded memory-mapped key-value store Overview LMDB (Lightning Memory-Mapped Database) is an embedded, in-process, memory-mapped ordered key-value store (no server). The environment is opened directly against a filesystem path and lives inside your process. It exposes ordered KV primitives — put/get, remove for deletes, and ordered range scans via getRange — but no query language and no SQL-shaped surface (query(sql), identifier escaping, DDL). Because none of that fits the SQL-shaped Dialect interface, LmdbStore implements the minimal NoSqlStore marker interface (src/nosql/store.ts) — connection lifecycle plus a getClient() escape hatch — and exposes LMDB's real KV surface directly. Identity: Property | Value | --------- | --------- | name | 'lmdb' | library | 'lmdb' | The store is built on the lmdb npm driver. Note that LMDB reads are synchronous — client.get(key) returns the value directly rather than a Promise — while put/remove may return a boolean or a Promise. Lazy loading — not a hard dependency The lmdb driver is not a hard dependency of this package. It is loaded lazily via require('lmdb') 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 LMDB store is actually connected without an injected client. On load, connect() calls lmdb.open({ path, ...config }). Injected client LmdbStoreOptions accepts a pre-built client (any object matching LmdbClientLike). When provided, connect() uses it directly and skips require('lmdb') entirely. This is how the test suite injects a mock database (no driver, no filesystem), and how callers can supply a custom-configured database. Connection Build a store from connection options and call connect(): Connection options (LmdbStoreOptions): Option | Type | Purpose | -------- | ----------------------------- | --------------------------------------------------------------------------------------------------------- | path | string | Filesystem path for the LMDB environment. Defaults to './data/lmdb'. | config | Record<string, unknown> | Escape hatch for any other lmdb open() option (compression, maxDbs, encoding, …). Merged into the object passed to open(). | client | LmdbClientLike | A pre-built database to use directly (mock or custom). When set, the driver is not required. | When no client is provided, connect() calls lmdb.open({ path: this.path, ...this.options.config }). Injected-client form Supply your own database (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 LMDB <action> failed: … message. Lifecycle Method | Signature | Behavior | ------------ | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | connect | connect(): Promise<void> | Uses an injected client if provided, otherwise lazy-requires lmdb and calls lmdb.open({ path, ...config }). Idempotent when already connected. Wraps failures in ConnectionError. | disconnect | disconnect(): Promise<void> | Best-effort client.close(), then clears connection state. | isConnected| isConnected(): boolean | true only when connected and a client is present. | getClient | getClient(): LmdbClientLike | Returns the underlying database 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. Routes to client.put(key, value) (awaited). | get | get(key: string): Promise<unknown> | Reads the value at key. Routes to client.get(key) — LMDB reads are synchronous, so the result is returned directly. | del | del(key: string): Promise<void> | Deletes the value at key via client.remove(key). | batch | batch(ops: LmdbBatchOp[]): Promise<void> | Maps each op onto client.put (put) or client.remove (del), collects the pending results, and awaits them with Promise.all. LMDB coalesces concurrent writes into one transaction. | list | list(prefix?: string): Promise<string[]> | Lists keys in order via client.getRange(). With no prefix, scans with {}. With prefix, restricts to the range { start: prefix, end: prefix + '\xff' }. | LmdbBatchOp is the entry type for a batch(): Example Verification status Unit / mock-verified only. The tests in tests/nosql/lmdb.test.ts are fully mock-driven: a fake database whose methods are jest spies (a synchronous get, a getRange returning a plain array of { key, value }) is injected via LmdbStoreOptions.client. The real lmdb 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('lmdb'). What this proves: Each method routes to the correct client call with the expected arguments (put(key, value), get(key), remove(key) for del). batch() maps put/del ops onto put/remove; list() collects keys from getRange, and list(prefix) builds the { start, end } range. Lifecycle: connecting via an injected client without loading the driver, disconnect() closing the database, idempotent re-connect, and ConnectionError before connect() / via getClient() when not connected. Error handling: put (rejected Promise) and synchronous get (thrown) failures wrapped in DatabaseError. What this does not prove: live execution against a real LMDB environment on disk. The call/response 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