0.2.1 • Published 1 year ago
@kevodb/kevo-sdk v0.2.1
🔑 Kevo TypeScript SDK
High-performance TypeScript/JavaScript client for the Kevo key-value store.
✨ Features
- 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)
🚀 Installation
npm install kevo-sdkOr install from source:
git clone https://github.com/KevoDB/typescript-sdk.git
cd typescript-sdk
npm install
npm run build🏁 Quick Start
JavaScript
const { 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);TypeScript
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);📖 API Reference
KevoClient
const client = new KevoClient(options);Constructor 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')
}Core Methods
| 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 |
Advanced Features
| 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 |
Transaction
Methods
| 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 |
BatchWriter
Methods
| 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 |
Scan Operations
// 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()}`);
}Error Handling
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 |
📋 Examples
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
🛠️ Development
Prerequisites
- Node.js 18+
- npm
Setup
# 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 typecheckGenerating Code from Proto Files
The 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