High-performance TypeScript/JavaScript client for the Kevo key-value store.
- Simple and intuitive API for JavaScript/TypeScript developers
- Efficient binary protocol (gRPC)
- Full TypeScript type definitions
- Transaction support with ACID guarantees
- Range and prefix scans using async iterators
- Atomic batch operations
- Buffer and string interface
- TLS/SSL support
- Automatic retries with exponential backoff
- Smart query routing (reads to replicas, writes to primary)
npm install kevo-sdkOr install from source:
git clone https://github.com/KevoDB/typescript-sdk.git
cd typescript-sdk
npm install
npm run buildconst { KevoClient, KeyNotFoundError } = require('kevo-sdk');
async function main() {
// Create a client
const client = new KevoClient({
host: 'localhost',
port: 50051,
// Smart query routing options
autoRouteReads: true, // Route reads to replicas when available
autoRouteWrites: true, // Route writes to primary
preferReplica: true // Prefer replicas for read operations
});
try {
// Connect to the database
await client.connect();
// Basic operations
await client.put('hello', 'world');
try {
const value = await client.get('hello');
console.log(value.toString()); // Prints: world
} catch (error) {
if (error instanceof KeyNotFoundError) {
console.log('Key not found');
} else {
throw error;
}
}
// Scan with prefix
for await (const { key, value } of client.scanPrefix('user:')) {
console.log(`Key: ${key.toString()}, Value: ${value.toString()}`);
}
// Use transactions
const tx = await client.beginTransaction();
try {
await tx.put('key1', 'value1');
await tx.put('key2', 'value2');
await tx.commit();
} catch (error) {
await tx.rollback();
throw error;
}
} finally {
// Always disconnect when done
client.disconnect();
}
}
main().catch(console.error);import { KevoClient, ConnectionOptions, KeyNotFoundError } from 'kevo-sdk';
async function main() {
// Create a client with type-safe options
const options: ConnectionOptions = {
host: 'localhost',
port: 50051,
// Optional settings
useTls: false,
connectTimeout: 5000,
requestTimeout: 10000,
maxRetries: 3,
// Smart query routing options
autoRouteReads: true,
autoRouteWrites: true,
preferReplica: true,
replicaSelectionStrategy: 'round_robin',
};
const client = new KevoClient(options);
try {
// Connect to the database
await client.connect();
// Basic operations (works with string or Buffer)
await client.put('counter', '1');
await client.put(Buffer.from('binary-key'), Buffer.from([0x01, 0x02, 0x03]));
// Get values
try {
// Read operations automatically route to replicas based on client configuration
const value = await client.get('counter');
console.log(`Counter: ${value.toString()}`);
const binaryValue = await client.get('binary-key');
console.log(`Binary: ${binaryValue.toString('hex')}`);
} catch (error) {
if (error instanceof KeyNotFoundError) {
console.log('Key not found');
} else {
throw error;
}
}
// Transaction example
const tx = await client.beginTransaction({ readOnly: false });
try {
const currentValue = await tx.get('counter');
const newValue = (parseInt(currentValue.toString()) + 1).toString();
await tx.put('counter', newValue);
await tx.commit();
} catch (error) {
await tx.rollback();
throw error;
}
} finally {
client.disconnect();
}
}
main().catch(console.error);const client = new KevoClient(options);interface ConnectionOptions {
host: string; // Required: The database host
port: number; // Required: The database port
useTls?: boolean; // Optional: Whether to use TLS (default: false)
caCert?: Buffer; // Optional: CA certificate for TLS
clientCert?: Buffer; // Optional: Client certificate for TLS
clientKey?: Buffer; // Optional: Client key for TLS
connectTimeout?: number; // Optional: Connection timeout in ms (default: 5000)
requestTimeout?: number; // Optional: Request timeout in ms (default: 10000)
maxRetries?: number; // Optional: Maximum number of retries (default: 3)
retryDelay?: number; // Optional: Base delay between retries in ms (default: 1000)
// Smart Query Routing Options
autoRouteReads?: boolean; // Optional: Auto-route reads to replicas (default: true)
autoRouteWrites?: boolean; // Optional: Auto-route writes to primary (default: true)
preferReplica?: boolean; // Optional: Prefer replicas for reads (default: true)
replicaSelectionStrategy?: 'random' | 'sequential' | 'round_robin'; // Optional: How to choose replicas (default: 'round_robin')
}| Method | Description |
|---|---|
connect() |
Connect to the server |
disconnect() |
Close the connection |
isConnected() |
Check if connected to the server |
get(key) |
Get a value by key |
put(key, value, sync?) |
Store a key-value pair |
delete(key, sync?) |
Delete a key-value pair |
| Method | Description |
|---|---|
batch() |
Create a new batch writer |
scanPrefix(prefix, options?) |
Scan for keys with a prefix |
scanRange(start, end, options?) |
Scan for keys in a range |
scan(options?) |
Low-level scan with custom options |
beginTransaction(options?) |
Begin a new transaction |
getStats() |
Get database statistics |
compact() |
Trigger database compaction |
| Method | Description |
|---|---|
getId() |
Get the transaction ID |
commit() |
Commit the transaction |
rollback() |
Roll back the transaction |
get(key) |
Get a value within the transaction |
put(key, value) |
Store a key-value pair within the transaction |
delete(key) |
Delete a key-value pair within the transaction |
scan(options?) |
Scan keys within the transaction |
isCommitted() |
Check if transaction is committed |
isRolledBack() |
Check if transaction is rolled back |
| Method | Description |
|---|---|
put(key, value) |
Add a put operation to the batch |
delete(key) |
Add a delete operation to the batch |
size() |
Get the number of operations in the batch |
clear() |
Clear all operations from the batch |
execute() |
Execute all operations atomically |
// Prefix scan
for await (const { key, value } of client.scanPrefix('user:')) {
console.log(`${key.toString()}: ${value.toString()}`);
}
// Range scan
for await (const { key, value } of client.scanRange('user:100', 'user:200')) {
console.log(`${key.toString()}: ${value.toString()}`);
}
// Scan with options
for await (const { key, value } of client.scan({
prefix: 'user:',
limit: 10,
reverse: true
// Read operations will automatically route to replicas based on client configuration
})) {
console.log(`${key.toString()}: ${value.toString()}`);
}The SDK provides several error classes for specific error cases:
| Error Class | Description |
|---|---|
KevoError |
Base error class |
ConnectionError |
Connection-related errors |
TimeoutError |
Timeout errors |
TransactionError |
Transaction-related errors |
KeyNotFoundError |
Key not found errors |
InvalidArgumentError |
Invalid argument errors |
ReadOnlyError |
Errors when attempting write operations on read-only replicas |
Check the examples directory for more detailed examples:
01-basic-operations.js: Basic key-value operations02-transactions.js: Transaction support with ACID guarantees03-scanning.js: Scanning operations with prefix and range
- Node.js 18+
- npm
# Install dependencies
npm install
# Build the SDK
npm run build
# Run tests
npm run test
# Run linters
npm run lint
# Check TypeScript types
npm run typecheckThe SDK uses gRPC for communication with the Kevo database. The TypeScript client code is generated from the Protocol Buffer definition file:
# Install protobuf compiler if needed
# For Ubuntu/Debian:
# sudo apt-get install protobuf-compiler
# For macOS:
# brew install protobuf
# Generate the TypeScript client code
npx grpc_tools_node_protoc \
--js_out=import_style=commonjs,binary:./src/proto \
--grpc_out=grpc_js:./src/proto \
--proto_path=./proto \
./proto/kevo/service.proto
# Or use the @grpc/proto-loader package as implemented in the SDK
# See connection.ts for implementation details