ImmudbStore — immudb immutable key-value ledger

Read this page in the documentation

ImmudbStore — immudb immutable key-value ledger Overview immudb is an immutable, append-only key-value (and SQL) database with built-in cryptographic verification: every write is added to a tamper-evident Merkle-tree-backed log, and verifiedSet/verifiedGet return an inclusion/consistency proof the client checks locally, so a compromised server cannot lie about stored data without detection. immudb is a key-value ledger, not a SQL Dialect, so ImmudbStore implements the minimal NoSqlStore marker interface (src/nosql/store.ts) — connection lifecycle plus a getClient() escape hatch — plus the shared ledger surface (append/get/history/verify/query). Because immudb addresses records by key rather than by (table, row), append/get/history/verify treat their first argument as a key namespace and compose the immudb key as <table>:<id> (or just <table> when no id is given). query/exec map to immudb's embedded SQL engine (sqlQuery/sqlExec). Identity: Property | Value | --------- | -------------- | name | 'immudb' | library | 'immudb-node'| Lazy loading — not a hard dependency immudb-node is not a hard dependency of this package. The client is loaded lazily via require() inside connect(), rather than a top-level import. Importing this module therefore does not require the package to be installed — it is only needed when an immudb store is actually connected. On construction, the module resolves the client constructor from mod.default ?? mod.ImmudbClient ?? mod, then (for a non-injected client) calls login() and, if a database was configured, useDatabase(). Injected client ImmudbStoreOptions accepts a pre-built, already-logged-in client (an ImmudbClientLike). When provided, connect() uses it verbatim and skips both the require and the login/useDatabase calls. This is how the test suite injects a mock client (no package, no network). Connection Build a store from connection options and call connect(). All options are optional. Option | Type | Purpose | ---------- | ------------------ | ----------------------------------------------------------------- | host | string | Server host. Defaults to '127.0.0.1'. | port | number | Server port. Defaults to 3322. | user | string | Login user. Defaults to 'immudb'. | password | string | Login password. Defaults to 'immudb'. | database | string | Database to useDatabase() after login. | client | ImmudbClientLike | A pre-built, logged-in client. When set, the package is not required and login/useDatabase are skipped. | Injected-client form Methods Every ledger method resolves the client, runs the underlying call, and wraps any failure in a DatabaseError (via DatabaseError.from, prefixing the message with the failing action). Using any method before connect() (or after disconnect()) throws a ConnectionError. Non-string data is JSON-encoded before being stored. Lifecycle Method | Signature | Behavior | ------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | connect | connect(): Promise<void> | Uses an injected client if provided, otherwise lazy-requires immudb-node, constructs the client, and logins (+ optional useDatabase). Idempotent. Wraps failures in ConnectionError. | disconnect | disconnect(): Promise<void> | Calls the client's optional close() or shutdown() (best-effort) and clears connection state. | isConnected | isConnected(): boolean | true only when connected and a client is present. | getClient | getClient(): ImmudbClientLike | Returns the underlying client for operations not wrapped here. Throws ConnectionError if not connected. | Ledger surface Method | Signature | immudb call | Behavior | --------- | --------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------- | append | append(table: string, data: unknown): Promise<string> | verifiedSet | Verified (tamper-evident) write under key <table>:<data.id> (or <table> when data has no id). Returns the write's transaction id/hash (transactionId ?? id ?? txId ?? tx, falling back to the key). | get | get(table: string, id?: string): Promise<unknown> | verifiedGet | Verified read of the current value at <table>:<id> (or <table>). | history | history(table: string, id?: string): Promise<unknown> | history | Returns every prior revision of the key <table>:<id> (or <table>). | verify | verify(table: string, id?: string): Promise<unknown> | verifiedGet | Verified read returning the full response — value plus the cryptographic inclusion proof. A resolved response (rather than a thrown verification error) is the proof of consistency. | query | query(statement: string, params?: unknown): Promise<unknown> | sqlQuery | Runs a SQL query against immudb's embedded SQL engine. Throws DatabaseError (code: 'QUERYERROR') if the client has no sqlQuery. | exec | exec(statement: string, params?: unknown): Promise<unknown> | sqlExec | Executes a SQL DDL/DML statement (no rows returned). Throws DatabaseError (code: 'QUERYERROR') if the client has no sqlExec. | Example Verification status Unit / mock-verified only. The tests in tests/nosql/immudb.test.ts inject a mock immudb client via ImmudbStoreOptions.client, so connect() never loads immudb-node and no network is touched. Jest spies assert each ledger method routes to the right verified/KV/SQL call with the composed <table>:<id> key. What this proves: append calls verifiedSet with the composed key and JSON-encoded value, and returns the response's transaction id ('42' in the mock); when data has no id, the key is the table itself. get/verify call verifiedGet with the composed key; verify's response carries verified: true. history calls history and returns the prior revisions. query/exec route to sqlQuery/sqlExec with { sql, params }. Lifecycle: connecting via injected client, idempotent double-connect, disconnect() calling close(), and ConnectionError from getClient() / reads before connect(). Error handling: client failures wrapped in DatabaseError. What this does not prove: live execution against a real immudb server, or actual local verification of the cryptographic inclusion/consistency proofs. The call/response shape is verified against the client's documented contract, but end-to-end execution over the wire 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