Node.js Client
The Node.js client is a fully async, Promise-based driver built on the built-in net/tls modules — no third-party dependencies — with a single-connection client and an async connection pool.
Install
bashnpm install veltrixdb-client
Requires Node.js 16+.
Quick start
Taken from the SDK's example.js:
javascriptconst { VeltrixDBClient } = require('veltrixdb-client'); async function main() { const c = new VeltrixDBClient({ host: '127.0.0.1', port: 9000 }); await c.connect(); await c.ping(); console.log('Connected to VeltrixDB'); // Put / Get / Delete await c.put('hello', Buffer.from('world')); const val = await c.get('hello'); console.log(`PUT+GET hello = ${val.toString()}`); await c.delete('hello'); const gone = await c.get('hello'); console.log(`GET after DEL hello = ${gone}`); // null c.close(); } main().catch(err => { console.error(`Error: ${err.message}`); process.exit(1); });
put accepts either a Buffer or a plain string (it's coerced internally with Buffer.from); get always returns a Buffer or null.
Config
javascriptnew VeltrixDBClient({ host: '127.0.0.1', port: 9000, timeout: 10000, // per-operation idle timeout (ms) connectTimeout: 5000, // connection timeout (ms) tls: null, // null = no TLS; object = tls.connect() options })
A single VeltrixDBClient is not safe for concurrent method calls — it processes one operation at a time over one socket. Use VeltrixDBPool when multiple call sites need concurrent access.
API surface
All methods return Promises.
Core key-value
| Method | Description |
|---|---|
connect() | Open the TCP connection |
ping() | Check connection health |
put(key, value) | Write a key (value can be Buffer or string) |
get(key) | Read a key. Returns Buffer or null |
delete(key) | Delete a key |
info() | Get server stats string |
auth(user, pass) | Authenticate |
close() | Close the connection |
Batch operations
| Method | Description |
|---|---|
multiPut(entries) | Write multiple keys. Returns array of errors (null = success) |
multiGet(keys) | Read multiple keys. Returns array of Buffer or null |
javascriptconst entries = [ { key: 'user:1', value: Buffer.from('{"name":"Alice","age":30}') }, { key: 'user:2', value: Buffer.from('{"name":"Bob","age":25}') }, { key: 'session:abc', value: Buffer.from('token-xyz'), ttl: 60 }, // 60s TTL ]; const putErrors = await c.multiPut(entries); const failures = putErrors.filter(Boolean); const keys = ['user:1', 'user:2', 'user:3', 'user:missing']; const values = await c.multiGet(keys); for (const [key, v] of keys.map((k, i) => [k, values[i]])) { console.log(v !== null ? `${key} = ${v.toString()}` : `${key} → not found`); }
Namespaces
| Method | Description |
|---|---|
putNS(ns, key, value, ttl) | Write into a namespace |
getNS(ns, key) | Read from a namespace |
deleteNS(ns, key) | Delete from a namespace |
scanNamespace(ns, prefix, limit) | Scan keys in a namespace |
listNamespaces() | List all namespaces and key counts |
dropNamespace(ns) | Delete all keys in a namespace |
Hash fields (per-field TTL)
| Method | Description |
|---|---|
hset(key, field, value, ttl=-1) | Set a field with its own TTL |
hget(key, field) | Get a field value (null if not found/expired) |
hdel(key, field) | Delete one field |
hgetAll(key) | Get all live fields as [{field, value}, ...] |
hkeys(key) | Get all live field names |
hlen(key) | Count live fields |
hexpire(key, field, ttl) | Update TTL of an existing field |
httl(key, field) | Remaining TTL in seconds (-1=forever, -2=not found) |
Atomic operations
New in v1.1.0 — typed atomic ops (previously Rust/C++ only). All are Promise-based; each takes an optional trailing ttl in seconds for the stored value (-1 = no expiry):
| Method | Description |
|---|---|
cas(key, expected, newValue, ttl=-1) | Compare-and-swap. Resolves to 'swapped', 'mismatch', or 'notfound' |
incr(key, delta=1, ttl=-1) | Atomically add delta; resolves to the new value (Number). Missing keys start from 0 |
decr(key, delta=1, ttl=-1) | Atomically subtract delta; resolves to the new value (Number). Missing keys start from 0 |
setnx(key, value, ttl=-1) | Set only if absent. Resolves to true when created, false when the key already existed |
javascript// Counter — missing key starts from 0 let n = await c.incr('counter', 5); // 5 n = await c.decr('counter', 2); // 3 // Compare-and-swap (expected/new accept Buffer or string) const outcome = await c.cas('hello', 'world', 'earth'); switch (outcome) { case 'swapped': console.log('replaced'); break; case 'mismatch': console.log('value did not match'); break; case 'notfound': console.log('key does not exist'); break; } // Set-if-absent with a 30-second TTL (lock-style) const acquired = await c.setnx('lock', 'owner-A', 30); if (!acquired) console.log('someone else holds the lock');
Connection pool
javascriptconst { VeltrixDBPool } = require('veltrixdb-client'); const pool = new VeltrixDBPool({ host: '127.0.0.1', port: 9000, poolSize: 8, // 8 connections acquireTimeout: 5000, // wait up to 5s for a free slot }); await pool.connect(); // 50 concurrent writes — pool distributes across 8 connections await Promise.all( Array.from({ length: 50 }, (_, i) => pool.execute(c => c.put(`concurrent:${i}`, Buffer.from(`value-${i}`))) ) ); // Functional style const user1 = await pool.execute(c => c.get('user:1')); console.log(`Pool available connections: ${pool.available}`); pool.close();
Each pooled connection handles one operation at a time; concurrent execute calls queue internally until a connection frees up, so throughput scales with poolSize up to your concurrency level.
TLS
javascriptconst fs = require('fs'); // Server verification const c = new VeltrixDBClient({ host: 'veltrix.example.com', port: 9443, tls: { ca: fs.readFileSync('ca.crt') }, }); // mTLS (client certificate) const c2 = new VeltrixDBClient({ host: 'veltrix.example.com', port: 9443, tls: { ca: fs.readFileSync('ca.crt'), cert: fs.readFileSync('client.crt'), key: fs.readFileSync('client.key'), }, });
Auth
javascriptconst c = new VeltrixDBClient({ host: '127.0.0.1', port: 9000 }); await c.connect(); await c.auth('alice', 'secret'); await c.put('key', Buffer.from('value')); // Pool with auto-auth const pool = new VeltrixDBPool({ host: '127.0.0.1', port: 9000, poolSize: 4, username: 'alice', password: 'secret', // all connections auto-auth on connect });
Error handling
javascriptconst { VeltrixDBError } = require('veltrixdb-client'); const val = await c.get('missing-key'); if (val === null) { console.log('not found'); } try { await c.put('key', Buffer.from('value')); } catch (err) { if (err instanceof VeltrixDBError) { console.error('server error:', err.message); } else if (err.code === 'ETIMEDOUT') { console.error('timed out'); } else if (err.code === 'ECONNREFUSED') { console.error('server not reachable'); } }
VeltrixDBError wraps server-side STATUS_ERR responses and protocol-level failures (e.g. a batch-level error on multiPut/multiGet). Socket-level failures surface as regular Node.js Error objects with a code such as ETIMEDOUT or ECONNREFUSED.