Skip to content

oxia-db/oxia-client-node

Repository files navigation

Oxia Node.js Client SDK

CI npm License

Node.js / TypeScript client for Oxia, a scalable metadata store and coordination system for large-scale distributed systems.

Getting Started | Documentation | TSDoc reference | npm | Discussion

Requirements

  • Node.js 20+

Install

npm install @oxia-db/client

Quick start

import { OxiaClient, EXPECTED_RECORD_DOES_NOT_EXIST } from '@oxia-db/client';

const client = await OxiaClient.connect('localhost:6648');
try {
  const { key, version } = await client.put('/users/alice', 'hello', {
    expectedVersionId: EXPECTED_RECORD_DOES_NOT_EXIST, // fail if already exists
  });

  const got = await client.get('/users/alice');
  console.log(got.key, new TextDecoder().decode(got.value), got.version.versionId);

  await client.delete('/users/alice');
} finally {
  await client.close();
}

Client options

await OxiaClient.connect('localhost:6648', {
  namespace: 'default',          // default: 'default'
  sessionTimeoutMs: 30_000,      // default: 30s; min 2000ms
  heartbeatIntervalMs: 3_000,    // default: ~sessionTimeoutMs/10, floored at 2s
  clientIdentifier: 'my-worker', // default: random UUID hex
  requestTimeoutMs: 30_000,      // default: 30s; applied to unary RPCs only
  initTimeoutMs: 30_000,         // time to wait for the first shard map
  authentication: undefined,     // see "Authentication" below
});

Operations

put / delete / get

await client.put('/k', 'v');
await client.put('/k', 'v2', { expectedVersionId: v1.version.versionId });
await client.put('/k', 'v',  { ephemeral: true });                  // removed when client closes
await client.put('/k', 'v',  { secondaryIndexes: { 'by-user': 'alice' } });

await client.delete('/k');
await client.delete('/k', { expectedVersionId: v.version.versionId });

const r = await client.get('/k');                                   // EQUAL
const r2 = await client.get('/k', { comparisonType: ComparisonType.FLOOR });
const r3 = await client.get('alice', { useIndex: 'by-user' });      // by secondary index

Range operations

// List all keys in a range (hierarchical ordering; see note below).
const keys = await client.list('/users/', '/users/~');

// Stream records in a range.
for await (const rec of client.rangeScan('/users/', '/users/~')) {
  console.log(rec.key, new TextDecoder().decode(rec.value));
}

// Delete a range.
await client.deleteRange('/tmp/', '/tmp/~');

With a partitionKey, operations are limited to a single shard:

await client.put('entry-a', 'v', { partitionKey: 'session-42' });
await client.list('entry-', 'entry-~', { partitionKey: 'session-42' });

Sequential keys

const r = await client.put('ticket', 'payload', {
  partitionKey: 'queue',
  sequenceKeysDeltas: [1],  // server assigns a zero-padded suffix
});
console.log(r.key); // -> "ticket-00000000000000000001"

Notifications

const notifications = client.getNotifications();
for await (const n of notifications) {
  console.log(n.type, n.key, n.versionId);
  if (shouldStop()) notifications.close(); // ends the loop
}

Sequence updates

const updates = client.getSequenceUpdates('ticket', { partitionKey: 'queue' });
for await (const latestKey of updates) {
  console.log('sequence advanced to', latestKey);
}

Authentication

import { OxiaClient, TokenAuthentication } from '@oxia-db/client';

// Static bearer token:
const client = await OxiaClient.connect('oxia.example.com:6648', {
  authentication: new TokenAuthentication('my-token'),
});

// Or a refresh-on-demand supplier:
const client2 = await OxiaClient.connect('oxia.example.com:6648', {
  authentication: new TokenAuthentication(async () => fetchTokenFromIdp()),
});

You can also implement the Authentication interface directly to add arbitrary gRPC metadata headers to every outgoing RPC:

import type { Authentication } from '@oxia-db/client';

class MyAuth implements Authentication {
  generateCredentials() {
    return { 'x-my-header': 'value' };
  }
}

Errors

All client errors extend OxiaError:

Error Meaning
KeyNotFoundError get / delete against a missing key
UnexpectedVersionIdError conditional put / delete saw a different version
SessionNotFoundError ephemeral put referenced a session the server has closed
InvalidOptionsError incompatible option combination (e.g. sequenceKeysDeltas without partitionKey)

Verifying the published package

Every release is published from a GitHub Actions run with SLSA provenance signed by Sigstore. You can verify that a given @oxia-db/client version came from this repository's release.yml workflow — with no long-lived npm token anywhere in the chain — before installing it:

npm audit signatures @oxia-db/client

npm also shows the provenance attestation on the package page under Provenance.

License

Apache License 2.0. See LICENSE and NOTICE.

About

Oxia Node.js Client SDK

Topics

Resources

License

Code of conduct

Contributing

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors