Skip to Content

Node.js Client SDK

Installing Node.js client SDK

npm install @oxia-db/client

The package is published on npm as @oxia-db/client. TSDoc reference documentation is available at https://oxia-db.github.io/oxia-client-node/api/ .

Client API

The Node.js client is fully asynchronous — every operation returns a Promise, and streaming operations return an AsyncIterable. It targets Node.js 18+ and ships with TypeScript type definitions.

Initializing the client

To use the Oxia client, create an instance via OxiaClient.connect(). Once created, a client can be used from multiple async contexts until close() is called.

import { OxiaClient } from '@oxia-db/client'; const client = await OxiaClient.connect('localhost:6648');

When creating the client it is possible to pass several options:

const client = await OxiaClient.connect('localhost:6648', { namespace: 'my-namespace', clientIdentifier: 'my-client-identity', });

Available OxiaClient.connect() options:

OptionTypeDescriptionDefault
namespacestringOxia namespace to use."default"
sessionTimeoutMsnumberSession timeout for ephemeral records, in milliseconds.30000
heartbeatIntervalMsnumberHeartbeat cadence. Must be < sessionTimeoutMs.max(sessionTimeoutMs/10, 2000ms)
clientIdentifierstringStable client identity attached to ephemeral records.Random UUID
requestTimeoutMsnumberDeadline for each unary RPC. Long-lived streams are unbounded.30000
initTimeoutMsnumberTimeout for the initial shard-assignment fetch.30000
authenticationAuthenticationCredentials attached as gRPC metadata. See TokenAuthentication.undefined

For the full parameter list see the OxiaClient.connect TSDoc .

Writing records

import { EXPECTED_RECORD_DOES_NOT_EXIST } from '@oxia-db/client'; // Write a record to Oxia with the specified key and value, and with the // expectation that the record does not already exist. const first = await client.put('my-key', 'value-1', { expectedVersionId: EXPECTED_RECORD_DOES_NOT_EXIST, }); // Write a record with the expectation that it has not changed since the // previous write. If there was any change, the operation will fail. const second = await client.put('my-key', 'value-2', { expectedVersionId: first.version.versionId, }); // Ephemeral record: deleted automatically when the client session ends. await client.put('/workers/worker-1', 'host:port', { ephemeral: true }); // Atomic sequence key: server appends a monotonic suffix; requires a partition key. const { key } = await client.put('/events/', 'event-data', { sequenceKeysDeltas: [1], partitionKey: '/events/', }); // Attach a secondary index entry at write time. await client.put('/offset/12345', '...', { secondaryIndexes: { partition: 'p-17' }, });

put() options:

OptionDescription
partitionKeyRoute to a specific shard (co-locate related keys).
expectedVersionIdConditional write; use EXPECTED_RECORD_DOES_NOT_EXIST to assert absence.
ephemeralBind the record to the client session — see ephemerals.
sequenceKeysDeltasServer-assigned monotonic suffixes — see sequence keys.
secondaryIndexes{ indexName: secondaryKey } — see secondary indexes.

Full reference: put() method TSDoc .

Reading records

Reading the value of a record:

import { ComparisonType } from '@oxia-db/client'; const { key, value, version } = await client.get('my-key'); console.log(new TextDecoder().decode(value)); // Metadata-only read (skips the value payload). const meta = await client.get('my-key', { includeValue: false }); // Range-style Get: returns the closest key ≤ the lookup key. const floor = await client.get('/users/50', { comparisonType: ComparisonType.FLOOR, });

get() options:

OptionDescription
comparisonTypeEQUAL (default), FLOOR, CEILING, LOWER, HIGHER. Non-equal modes scan all shards unless partitionKey is set.
includeValueSet to false for a metadata-only read.
partitionKeyRoute to a specific shard.
useIndexLook up via a named secondary index.

Full reference: get() method TSDoc .

Deleting records

Delete a single record by key. Supports conditional deletes using version-based expectations.

// Unconditional delete await client.delete('my-key'); // Conditional delete: only succeed if the version matches await client.delete('my-key', { expectedVersionId: version.versionId });

All the options for the delete operation are available in the delete() method TSDoc .

Deleting a range of records

Delete all records whose keys fall within [min, max):

await client.deleteRange('/users/', '/users//');

Without partitionKey, the call fans out to every shard. Pass partitionKey to scope the delete to a single shard.

Listing keys

List keys in [min, max) without fetching values:

const keys = await client.list('/users/', '/users//'); for (const key of keys) { console.log(key); } // Narrow to a single shard via partitionKey, or query a secondary index via useIndex. const scoped = await client.list('/users/', '/users//', { partitionKey: '/users/' }); const byEmail = await client.list('', '\xff', { useIndex: 'email' });

Scanning records

Scan records in a key range, returning both keys and values. The result is an AsyncIterable, so records stream as they arrive — break out of the loop to cancel the underlying server streams.

const decoder = new TextDecoder(); for await (const { key, value, version } of client.rangeScan('/users/', '/users//')) { console.log(`key: ${key}, value: ${decoder.decode(value)}, version: ${version.versionId}`); } // With a secondary index. for await (const { key } of client.rangeScan('', '\xff', { useIndex: 'email' })) { console.log(key); }

list() and rangeScan() both accept partitionKey and useIndex.

Sessions and ephemerals

Sessions are managed transparently: the first put(..., { ephemeral: true }) creates a per-shard session, the client SDK heartbeats it, and ephemerals are cleaned up when the client closes or the session expires. See ephemerals for the lifecycle details.

Notifications and sequence updates

Both streams are exposed as CloseableAsyncIterable<T> — iterate with for await (...) and call .close() when you’re done to end the subscription.

// Change feed for the namespace. const notifications = client.getNotifications(); for await (const n of notifications) { console.log(n.type, n.key, n.versionId); if (shouldStop()) notifications.close(); } // Updates for a specific sequence prefix. const updates = client.getSequenceUpdates('/events/', { partitionKey: '/events/' }); for await (const latestKey of updates) { console.log(`New sequence key: ${latestKey}`); }

See notifications and sequence keys for semantics.

For the complete reference see the TSDoc reference .

Last updated on