AerospikeStore — Aerospike key-value store

Read this page in the documentation

AerospikeStore — Aerospike key-value store Overview Aerospike is a distributed, flash-optimized key-value store. Records are addressed by a (namespace, set, key) tuple and hold a flat map of named bins (Aerospike's term for columns/fields). It has no query language and no SQL-shaped surface (no query(sql), identifier escaping, or DDL), so it does not fit the Dialect interface used by the SQL dialects in this repo. Instead, AerospikeStore implements the minimal NoSqlStore marker interface (src/nosql/store.ts) — connection lifecycle plus a getClient() escape hatch — and exposes Aerospike's real record primitives (put / get / remove / exists / operate / truncate) addressed by key, directly. Identity: Property | Value | --------- | ------------- | name | 'aerospike' | library | 'aerospike' | The store is built on the official aerospike npm driver: connect() calls Aerospike.connect({ hosts, ...config }) to obtain a client. Key addressing A key may be given as a bare string/number (resolved against the store's default namespace/set), or as an explicit { namespace?, set?, key } object that overrides either default per call. makeKey() builds the driver's key object, filling in defaults for anything omitted. When the real driver is loaded it returns a new Aerospike.Key(ns, set, key); when a mock client is injected (no module loaded) it returns a plain { ns, set, key } tuple. Lazy loading — not a hard dependency aerospike is an optional peer dependency, loaded lazily via require('aerospike') inside connect(), rather than a top-level import. Importing this module therefore never forces the driver to be installed — it is only needed when an Aerospike store is actually connected against a real server. Injected client AerospikeStoreOptions accepts a pre-built client (an aerospike client, or a compatible AerospikeClientLike mock). When provided, connect() uses it directly and never touches the real driver (require('aerospike') is skipped). This is how the test suite injects a mock (no driver, no network), and how callers can supply a custom-configured client. Connection Build a store from connection options and call connect(): AerospikeStoreOptions: Option | Type | Purpose | ----------- | ----------------------------- | -------------------------------------------------------------------------------------------------- | hosts | AerospikeHost[] | One or more hosts, { addr, port? }, e.g. [{ addr: '127.0.0.1', port: 3000 }]. | namespace | string | Default namespace applied to keys that don't specify one. Defaults to ''. | set | string | Default set applied to keys that don't specify one. Defaults to ''. | config | Record<string, unknown> | Escape hatch for any other aerospike config field (policies, auth, TLS). Merged into connect(). | client | AerospikeClientLike | A pre-built client (or mock). When set, the driver is never required. | connect() is idempotent: if already connected with a live client, it returns immediately. Connection failures are wrapped in a ConnectionError (message Unable to connect to Aerospike: ...), and connection state is reset. Injected-client form Supply your own client (or a mock) to bypass driver-based client construction: Methods Using any record operation before connect() (or after disconnect()) throws a ConnectionError via the internal requireClient() guard. Operation failures are wrapped in a DatabaseError (message Aerospike <action> failed: ..., preserving the original error). Lifecycle Method | Signature | Behavior | ------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | connect | connect(): Promise<void> | Uses an injected client if provided, otherwise lazy-requires aerospike and calls Aerospike.connect({ hosts, ...config }). Idempotent. Wraps failures in ConnectionError. | disconnect | disconnect(): Promise<void> | Calls client.close(false) and clears the client, driver module reference, and connection state. Safe to call twice. | isConnected | isConnected(): boolean | true only when connected and a client is present. | getClient | getClient(): AerospikeClientLike | Returns the underlying client for operations not wrapped here. Throws ConnectionError if not connected. | Key building Method | Signature | Behavior | --------- | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | makeKey | makeKey(input: AerospikeKeyInput): AerospikeKeyTuple \| unknown | Builds the driver's key object from input, filling in the store's default namespace/set for anything omitted. Returns a new Aerospike.Key(...) when the real driver is loaded, or a plain { ns, set, key } tuple otherwise. | Record operations Each operation builds its key via makeKey(key) before calling the driver. Method | Signature | Behavior | ---------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | put | put(key: AerospikeKeyInput, bins: Record<string, unknown>, meta?: AerospikeMeta): Promise<unknown> | Writes a record's bins. Optionally forwards write metadata (meta) to the driver. Returns the driver's result. | get | get(key: AerospikeKeyInput): Promise<Record<string, unknown>> | Reads a record, returning its record.bins. Throws (wrapped) if the record doesn't exist. | remove | remove(key: AerospikeKeyInput): Promise<unknown> | Removes a record by key. Returns the driver's result. | exists | exists(key: AerospikeKeyInput): Promise<boolean> | Returns whether a record exists for the key. | operate | operate(key: AerospikeKeyInput, ops: unknown[]): Promise<unknown> | Runs an ordered list of operations atomically against a single record (increment, append, read-back, etc). ops is passed straight through — build them with the driver's operation helpers. | truncate | truncate(set?: string): Promise<unknown> | Truncates a set (or the store's default set), deleting every record in it. Calls client.truncate(namespace, set ?? this.set ?? null, 0) (0 = everything up to now). Throws a DatabaseError if the client has no truncate(). | Example Verification status Unit / mock-verified only. The tests in tests/nosql/aerospike.test.ts are pure unit tests: a fake client whose put/get/remove/exists/operate/truncate/close are jest spies is injected via AerospikeStoreOptions.client. The real aerospike driver is not installed and there is no network — because a client is injected, connect() never reaches require('aerospike'), so makeKey() returns the plain { ns, set, key } tuple shape (which the tests assert on). What this proves: makeKey() builds the { ns, set, key } tuple from defaults for bare string/number keys, and lets an explicit key object override or fall back to defaults for namespace/set. Each record operation routes to the matching client method with makeKey(key) as the key: put (with and without meta), get (returning record.bins), remove, exists, operate (passing ops straight through), and truncate (forwarding namespace + set, defaulting to the configured set, with beforeNanos 0). Lifecycle: connecting via the injected client without loading the driver, idempotent connect/disconnect, disconnect() calling client.close(), and ConnectionError from getClient() / operations before connect(). Error handling: put/get failures wrapped in DatabaseError. What this does not prove: live execution against a real Aerospike server, nor the real-driver makeKey() path that builds an Aerospike.Key. The call shapes are verified against the aerospike API contract, but end-to-end record access 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