Skip to main content
Version: Next

JavaScript/Node.js SDK Guide

🔧 Skill Level: Beginner
⏱️ Time Required: 30 minutes
📦 Requirements: Node.js 18+, TypeScript 5+ Version: 0.5.1

Complete guide to SochDB's JavaScript SDK with dual-mode architecture (embedded FFI + server gRPC), namespaces, collections, vector search, priority queues, MCP integration, and advanced features.


Table of Contents

  1. Installation
  2. Quick Start
  3. Architecture: Dual-Mode
  4. Namespace & Collections
  5. Vector Search
  6. Priority Queue
  7. Semantic Cache
  8. Context Builder
  9. Memory System
  10. MCP Integration
  11. Policy Service
  12. Core Operations
  13. Transactions
  14. Server Mode (gRPC/IPC)
  15. Best Practices

📦 Installation

npm install @sochdb/sochdb
# or
yarn add @sochdb/sochdb

What's New in 0.5.1:

  • ✅ Improved TypeScript definitions
  • ✅ Better error messages

What's New in 0.4.3:

  • ✅ MCP (Model Context Protocol) client/server
  • ✅ Policy Service for access control
  • ✅ Enhanced namespace isolation

What's New in 0.4.2:

  • ✅ Memory System: Extraction, Consolidation, Retrieval
  • ✅ Hybrid retrieval with BM25 + vector

What's New in 0.4.1:

  • ✅ Namespace & Collection APIs
  • ✅ Priority Queue with ordered keys
  • ✅ Semantic Cache for LLM responses
  • ✅ Context Builder with token limits
  • ✅ Concurrent mode (multi-process MVCC)

Package includes:

  • Native binaries for all major platforms
  • Full TypeScript definitions
  • CLI tools: sochdb-server, sochdb-bulk, sochdb-grpc-server

🚀 Quick Start

import { Database } from 'sochdb';

async function main() {
// Open database with embedded engine
const db = await Database.open('./my_database');

try {
// Put and Get
await db.put(Buffer.from('user:123'), Buffer.from('{"name":"Alice","age":30}'));
const value = await db.get(Buffer.from('user:123'));
console.log(value.toString());
// Output: {"name":"Alice","age":30}
} finally {
await db.close();
}
}

main();

Output:

{"name":"Alice","age":30}

Embedded vs External

Embedded Mode (Default)

Runs SochDB engine in-process:

const db = await Database.open('./my_db');
// ✅ Fast: No IPC overhead
// ✅ Simple: Single process
// ❌ Limited: One connection per database

External Mode

Connects to standalone server:

# Terminal 1: Start server
sochdb-server --db ./my_database --host 127.0.0.1 --port 5555
// Terminal 2: Connect
import { IpcClient } from 'sochdb';

const client = await IpcClient.connect({
host: '127.0.0.1',
port: 5555
});

await client.put(Buffer.from('key'), Buffer.from('value'));
// ✅ Multi-process: Many clients
// ✅ Remote: Network access
// ❌ Slower: IPC overhead

When to use:

  • Embedded: Single app, local data, fast operations
  • External: Microservices, multi-process, remote data

Architecture: Dual-Mode

SochDB Node.js SDK supports three deployment modes:

1. Embedded Mode (FFI)

Direct FFI bindings to Rust libraries. No server required.

import { Database } from '@sochdb/sochdb';

const db = Database.open('./mydb');
await db.put(Buffer.from('key'), Buffer.from('value'));
await db.close();

2. Concurrent Mode (FFI + MVCC)

Multi-process access with MVCC. Ideal for PM2 clusters, Express workers.

import { openConcurrent } from '@sochdb/sochdb';

// Multiple processes can access simultaneously
const db = openConcurrent('./shared_db');
console.log(`Concurrent: ${db.isConcurrent}`); // true

3. Server Mode (gRPC)

Thin client connecting to sochdb-grpc server.

import { SochDBClient } from '@sochdb/sochdb';

const client = new SochDBClient({ address: 'localhost:50051' });
await client.putKv('namespace', 'key', Buffer.from('value'));
await client.close();

Namespace & Collections

New in v0.4.1 — Type-safe multi-tenant isolation with vector collections.

Creating Namespaces

import { Database, Namespace, NamespaceConfig } from '@sochdb/sochdb';

const db = Database.open('./multi_tenant');

// Create namespace
const config: NamespaceConfig = {
name: 'tenant_123',
displayName: 'Acme Corp',
labels: { tier: 'enterprise' },
};
const ns = db.createNamespace(config);

// Or get existing
const existingNs = db.namespace('tenant_123');

Creating Collections

import { CollectionConfig, DistanceMetric } from '@sochdb/sochdb';

const config: CollectionConfig = {
name: 'documents',
dimension: 384,
metric: DistanceMetric.COSINE,
m: 16,
efConstruction: 100,
};
const collection = ns.createCollection(config);

// Or simpler
const simpleCollection = ns.createCollection('embeddings', { dimension: 768 });

Vector Operations

// Insert vectors
await collection.insert({
vector: [0.1, 0.2, 0.3, /* ... */],
metadata: { source: 'web', url: 'https://...' },
id: 'doc_001',
});

// Batch insert
await collection.insertBatch({
vectors: [[0.1, ...], [0.2, ...], [0.3, ...]],
metadatas: [{ type: 'a' }, { type: 'b' }, { type: 'c' }],
ids: ['doc_1', 'doc_2', 'doc_3'],
});
import { SearchRequest } from '@sochdb/sochdb';

// Vector search
const results = await collection.search({
vector: queryEmbedding,
k: 10,
filter: { source: 'web' },
});

// Hybrid search (vector + keyword)
const hybridResults = await collection.search({
vector: queryEmbedding,
textQuery: 'machine learning',
k: 10,
alpha: 0.7, // 70% vector, 30% keyword
});

for (const result of results) {
console.log(`ID: ${result.id}, Score: ${result.score.toFixed(4)}`);
}

Priority Queue

New in v0.4.1 — First-class queue API with ordered-key task entries.

import { createQueue, TaskState } from '@sochdb/sochdb';

const db = Database.open('./queue_db');
const queue = createQueue(db, 'tasks', {
visibilityTimeoutMs: 30000,
maxAttempts: 3,
});

// Enqueue
const taskId = await queue.enqueue({
priority: 1, // Lower = higher priority
payload: Buffer.from(JSON.stringify({ action: 'process', orderId: 123 })),
});

// Dequeue and process
const task = await queue.dequeue('worker-1');
if (task) {
try {
// Process task...
await queue.ack(task.taskId);
} catch (error) {
await queue.nack(task.taskId);
}
}

// Stats
const stats = await queue.stats();
console.log(`Pending: ${stats.pending}, In-flight: ${stats.claimed}`);

Semantic Cache

New in v0.4.1 — Cache LLM responses with semantic similarity lookup.

import { SemanticCache } from '@sochdb/sochdb';

const cache = new SemanticCache(db, 'llm_cache', {
dimension: 1536,
similarityThreshold: 0.95,
ttlMs: 3600000, // 1 hour
});

// Check cache before calling LLM
const query = 'What is the capital of France?';
const queryEmbedding = await embed(query);

const hit = await cache.get(queryEmbedding);
if (hit) {
console.log('Cache hit:', hit.response);
} else {
const response = await callLLM(query);
await cache.set(queryEmbedding, response, { query });
console.log('Cache miss, stored:', response);
}

Context Builder

New in v0.4.1 — Token-aware context assembly for LLM prompts.

import { createContextBuilder, ContextOutputFormat } from '@sochdb/sochdb';

const builder = createContextBuilder({
maxTokens: 4000,
format: ContextOutputFormat.TOON, // Token-optimized
});

const context = await builder
.addSection('user', await db.get(Buffer.from('user:123')))
.addSection('history', await db.scan('messages/'))
.addSection('knowledge', await collection.search({ vector: queryVec, k: 5 }))
.build();

console.log(`Tokens used: ${context.tokenCount}`);
console.log(context.content);

Memory System

New in v0.4.2 — Structured memory extraction, consolidation, and retrieval.

Extraction

import { ExtractionPipeline } from '@sochdb/sochdb';

const pipeline = new ExtractionPipeline({
schema: {
entities: ['Person', 'Organization', 'Product'],
relations: ['works_at', 'owns', 'purchased'],
},
});

const result = await pipeline.extract(
'Alice works at Acme Corp and purchased a new laptop.'
);

console.log(result.entities);
// [{ type: 'Person', name: 'Alice' }, { type: 'Organization', name: 'Acme Corp' }, ...]
console.log(result.relations);
// [{ type: 'works_at', from: 'Alice', to: 'Acme Corp' }, ...]

Consolidation

import { Consolidator } from '@sochdb/sochdb';

const consolidator = new Consolidator(db, 'memory');

// Consolidate extracted facts
await consolidator.add(result);

// Merge duplicate entities
await consolidator.consolidate({
mergeThreshold: 0.9,
});

Hybrid Retrieval

import { HybridRetriever } from '@sochdb/sochdb';

const retriever = new HybridRetriever(db, 'memory', {
vectorWeight: 0.7,
keywordWeight: 0.3,
});

const memories = await retriever.retrieve({
query: 'What did Alice buy?',
queryVector: queryEmbedding,
k: 10,
});

for (const memory of memories) {
console.log(`${memory.content} (score: ${memory.score.toFixed(3)})`);
}

MCP Integration

New in v0.4.3 — Model Context Protocol for Claude and LLM agents.

MCP Server

import { McpServer } from '@sochdb/sochdb';

const server = new McpServer({
name: 'sochdb-mcp',
version: '1.0.0',
database: db,
});

// Register tools
server.registerTool({
name: 'search_documents',
description: 'Search documents by semantic similarity',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string' },
k: { type: 'number', default: 10 },
},
},
handler: async (input) => {
const results = await collection.search({ textQuery: input.query, k: input.k });
return { results };
},
});

await server.start({ transport: 'stdio' });

MCP Client

import { McpClient } from '@sochdb/sochdb';

const client = new McpClient({ transport: 'stdio' });

// Call a tool
const result = await client.callTool('search_documents', {
query: 'machine learning',
k: 5,
});
console.log(result);

Policy Service

New in v0.4.3 — Access control policies for namespaces and collections.

import { PolicyService, PolicyAction } from '@sochdb/sochdb';

const policyService = new PolicyService(db);

// Define policy
await policyService.createPolicy({
name: 'tenant_isolation',
rules: [
{
action: PolicyAction.READ,
resource: 'namespace:tenant_*',
condition: { 'user.tenantId': { $eq: '$resource.tenantId' } },
effect: 'allow',
},
],
});

// Evaluate access
const allowed = await policyService.evaluate({
action: PolicyAction.READ,
resource: 'namespace:tenant_123',
context: { user: { tenantId: 'tenant_123' } },
});

console.log(`Access allowed: ${allowed}`);

Core Operations

Basic K-V Operations

const db = await Database.open('./my_db');

// Put
await db.put(Buffer.from('key'), Buffer.from('value'));

// Get
const value = await db.get(Buffer.from('key'));
console.log(value?.toString());
// Output: value

// Delete
await db.delete(Buffer.from('key'));

// Get after delete
const deletedValue = await db.get(Buffer.from('key'));
console.log(deletedValue);
// Output: null

Output:

value
null

JSON Operations

// Store JSON
const user = { name: 'Alice', email: 'alice@example.com', age: 30 };
await db.put(
Buffer.from('users/alice'),
Buffer.from(JSON.stringify(user))
);

// Retrieve JSON
const value = await db.get(Buffer.from('users/alice'));
if (value) {
const retrievedUser = JSON.parse(value.toString());
console.log(`Name: ${retrievedUser.name}, Age: ${retrievedUser.age}`);
}

Output:

Name: Alice, Age: 30

Path API

Fixed in 0.2.6 — Now uses correct wire format:

// Store hierarchical data
await db.putPath('users/alice/email', Buffer.from('alice@example.com'));
await db.putPath('users/alice/age', Buffer.from('30'));
await db.putPath('users/alice/settings/theme', Buffer.from('dark'));

// Retrieve by path
const email = await db.getPath('users/alice/email');
console.log(`Alice's email: ${email?.toString()}`);

// Output: Alice's email: alice@example.com

Output:

Alice's email: alice@example.com

Path Format (Wire Protocol):

[path_count: 2 bytes LE]
[path_length_1: 2 bytes LE][path_1: UTF-8]
[path_length_2: 2 bytes LE][path_2: UTF-8]
...

Prefix Scanning

New in 0.2.6 — Multi-tenant isolation:

// Insert multi-tenant data
await db.put(Buffer.from('tenants/acme/users/1'), Buffer.from('{"name":"Alice"}'));
await db.put(Buffer.from('tenants/acme/users/2'), Buffer.from('{"name":"Bob"}'));
await db.put(Buffer.from('tenants/acme/orders/1'), Buffer.from('{"total":100}'));
await db.put(Buffer.from('tenants/globex/users/1'), Buffer.from('{"name":"Charlie"}'));

// Scan only ACME Corp data
const acmeData = [];
for await (const [key, value] of db.scan(
Buffer.from('tenants/acme/'),
Buffer.from('tenants/acme;')
)) {
acmeData.push([key.toString(), value.toString()]);
}

console.log(`ACME Corp has ${acmeData.length} items:`);
for (const [key, value] of acmeData) {
console.log(` ${key}: ${value}`);
}

Output:

ACME Corp has 3 items:
tenants/acme/orders/1: {"total":100}
tenants/acme/users/1: {"name":"Alice"}
tenants/acme/users/2: {"name":"Bob"}

Why use scan():

  • Fast: O(|prefix|) — only reads matching keys
  • Isolated: Perfect for multi-tenancy
  • Efficient: No full-table scan

Range trick:

// Scan "users/" to "users;" (semicolon is after '/' in ASCII)
const start = Buffer.from('users/');
const end = Buffer.from('users;');
// Matches: users/1, users/2, users/alice, ...
// Excludes: user, users, usersabc

Transactions

Automatic Transactions

// Atomic operations
const txn = await db.beginTransaction();
try {
await txn.put(Buffer.from('account:1:balance'), Buffer.from('1000'));
await txn.put(Buffer.from('account:2:balance'), Buffer.from('500'));

await txn.commit();
console.log('✅ Transaction committed');
} catch (error) {
await txn.abort();
console.error('❌ Transaction aborted:', error);
throw error;
}

Output:

✅ Transaction committed

Transaction with Scan

const txn = await db.beginTransaction();
try {
await txn.put(Buffer.from('key1'), Buffer.from('value1'));
await txn.put(Buffer.from('key2'), Buffer.from('value2'));

// Scan within transaction
for await (const [key, value] of txn.scan(
Buffer.from('key'),
Buffer.from('key~')
)) {
console.log(`${key.toString()}: ${value.toString()}`);
}

await txn.commit();
} catch (error) {
await txn.abort();
throw error;
}

Output:

key1: value1
key2: value2

Query Builder

Returns results in TOON format (token-optimized):

// Insert structured data
await db.put(
Buffer.from('products/laptop'),
Buffer.from('{"name":"Laptop","price":999,"stock":5}')
);
await db.put(
Buffer.from('products/mouse'),
Buffer.from('{"name":"Mouse","price":25,"stock":20}')
);

// Query with column selection
const results = await db.query('products/')
.select(['name', 'price'])
.limit(10)
.toList();

for (const [key, value] of results) {
console.log(`${key.toString()}: ${value.toString()}`);
}

Output (TOON Format):

products/laptop: result[1]{name,price}:Laptop,999
products/mouse: result[1]{name,price}:Mouse,25

TOON format benefits:

  • Fewer tokens for LLMs
  • Structured output
  • Easy parsing

TypeScript Usage

Type-Safe Operations

import { Database, Transaction } from 'sochdb';

interface User {
name: string;
email: string;
age: number;
}

async function main() {
const db = await Database.open('./my_db');

try {
// Store with type safety
const user: User = {
name: 'Alice',
email: 'alice@example.com',
age: 30
};

await db.put(
Buffer.from('users/alice'),
Buffer.from(JSON.stringify(user))
);

// Retrieve with type safety
const value = await db.get(Buffer.from('users/alice'));
if (value) {
const retrievedUser: User = JSON.parse(value.toString());
console.log(`User: ${retrievedUser.name} (${retrievedUser.email})`);
}
} finally {
await db.close();
}
}

Output:

User: Alice (alice@example.com)

Generic Helper Functions

class TypedDatabase {
constructor(private db: Database) {}

async putJSON<T>(key: string, value: T): Promise<void> {
await this.db.put(
Buffer.from(key),
Buffer.from(JSON.stringify(value))
);
}

async getJSON<T>(key: string): Promise<T | null> {
const value = await this.db.get(Buffer.from(key));
return value ? JSON.parse(value.toString()) : null;
}

async scanJSON<T>(prefix: string): Promise<Array<[string, T]>> {
const results: Array<[string, T]> = [];
for await (const [key, value] of this.db.scan(
Buffer.from(prefix),
Buffer.from(prefix.replace(/\/$/, ';'))
)) {
results.push([
key.toString(),
JSON.parse(value.toString())
]);
}
return results;
}
}

// Usage
const db = await Database.open('./my_db');
const typedDb = new TypedDatabase(db);

await typedDb.putJSON('users/alice', { name: 'Alice', age: 30 });
const user = await typedDb.getJSON<User>('users/alice');

Best Practices

1. Always Close Database

// ✅ Good: Use try-finally
const db = await Database.open('./my_db');
try {
await db.put(Buffer.from('key'), Buffer.from('value'));
} finally {
await db.close();
}

// ❌ Bad: Might leak resources
const db = await Database.open('./my_db');
await db.put(Buffer.from('key'), Buffer.from('value'));
// Forgot to close!

2. Use scan() for Prefix Queries

// ✅ Good: Efficient prefix scan
const results = [];
for await (const [key, value] of db.scan(
Buffer.from('users/'),
Buffer.from('users;')
)) {
results.push([key, value]);
}

// ❌ Bad: Load all keys into memory
const allKeys = await db.getAllKeys(); // Don't do this!
const filtered = allKeys.filter(k => k.startsWith('users/'));

3. Use Transactions for Atomicity

// ✅ Good: Atomic updates
const txn = await db.beginTransaction();
try {
await txn.put(Buffer.from('counter'), Buffer.from('1'));
await txn.put(Buffer.from('timestamp'), Buffer.from(Date.now().toString()));
await txn.commit();
} catch (error) {
await txn.abort();
throw error;
}

// ❌ Bad: Partial updates possible
await db.put(Buffer.from('counter'), Buffer.from('1'));
// If error here, counter is updated but timestamp isn't
await db.put(Buffer.from('timestamp'), Buffer.from(Date.now().toString()));

4. Handle Errors Properly

// ✅ Good: Proper error handling
try {
const value = await db.get(Buffer.from('key'));
if (value === null) {
console.log('Key not found');
} else {
console.log('Value:', value.toString());
}
} catch (error) {
console.error('Database error:', error);
}

// ❌ Bad: Assuming success
const value = await db.get(Buffer.from('key'));
console.log(value.toString()); // Crashes if null!

5. Use Buffer for Binary Data

// ✅ Good: Binary-safe
await db.put(Buffer.from('key'), Buffer.from([0x00, 0x01, 0x02]));

// ❌ Bad: String encoding issues
await db.put('key', '\x00\x01\x02'); // May corrupt data

Complete Examples

Example 1: Multi-Tenant SaaS Application

import { Database } from 'sochdb';

interface TenantUser {
id: string;
role: string;
email: string;
}

async function main() {
const db = await Database.open('./saas_db');

try {
// Insert tenant-specific data
const tenants = [
{ id: 'acme', name: 'ACME Corp' },
{ id: 'globex', name: 'Globex Inc' }
];

// ACME Corp users
await db.put(
Buffer.from('tenants/acme/users/alice'),
Buffer.from(JSON.stringify({ id: 'alice', role: 'admin', email: 'alice@acme.com' }))
);
await db.put(
Buffer.from('tenants/acme/users/bob'),
Buffer.from(JSON.stringify({ id: 'bob', role: 'user', email: 'bob@acme.com' }))
);

// Globex Inc users
await db.put(
Buffer.from('tenants/globex/users/charlie'),
Buffer.from(JSON.stringify({ id: 'charlie', role: 'admin', email: 'charlie@globex.com' }))
);

// Query each tenant's data in isolation
for (const tenant of tenants) {
const prefix = Buffer.from(`tenants/${tenant.id}/users/`);
const end = Buffer.from(`tenants/${tenant.id}/users;`);

const users: TenantUser[] = [];
for await (const [key, value] of db.scan(prefix, end)) {
users.push(JSON.parse(value.toString()));
}

console.log(`\n${tenant.name} (${users.length} users):`);
for (const user of users) {
console.log(` ${user.email} (${user.role})`);
}
}
} finally {
await db.close();
}
}

main().catch(console.error);

Output:

ACME Corp (2 users):
alice@acme.com (admin)
bob@acme.com (user)

Globex Inc (1 users):
charlie@globex.com (admin)

Example 2: SQL-Like Operations with K-V

import { Database } from 'sochdb';

interface Product {
id: string;
name: string;
price: number;
category: string;
}

async function main() {
const db = await Database.open('./ecommerce');

try {
// INSERT: Store products
const products: Product[] = [
{ id: '1', name: 'Laptop', price: 999.99, category: 'Electronics' },
{ id: '2', name: 'Mouse', price: 25.00, category: 'Electronics' },
{ id: '3', name: 'Desk', price: 299.99, category: 'Furniture' }
];

for (const product of products) {
await db.put(
Buffer.from(`products/${product.id}`),
Buffer.from(JSON.stringify(product))
);
}

// SELECT: Retrieve all products
console.log('All Products:');
for await (const [key, value] of db.scan(
Buffer.from('products/'),
Buffer.from('products;')
)) {
const product: Product = JSON.parse(value.toString());
console.log(` ${product.name}: $${product.price}`);
}

// WHERE: Filter by category
console.log('\nElectronics:');
for await (const [key, value] of db.scan(
Buffer.from('products/'),
Buffer.from('products;')
)) {
const product: Product = JSON.parse(value.toString());
if (product.category === 'Electronics') {
console.log(` ${product.name}: $${product.price}`);
}
}

// UPDATE: Modify price
const laptopValue = await db.get(Buffer.from('products/1'));
if (laptopValue) {
const laptop: Product = JSON.parse(laptopValue.toString());
laptop.price = 899.99;
await db.put(
Buffer.from('products/1'),
Buffer.from(JSON.stringify(laptop))
);
console.log(`\nUpdated ${laptop.name} price to $${laptop.price}`);
}

// DELETE: Remove product
await db.delete(Buffer.from('products/2'));
console.log('Deleted Mouse');

} finally {
await db.close();
}
}

main().catch(console.error);

Output:

All Products:
Laptop: $999.99
Mouse: $25
Desk: $299.99

Electronics:
Laptop: $999.99
Mouse: $25

Updated Laptop price to $899.99
Deleted Mouse

Example 3: Session Cache

import { Database } from 'sochdb';

interface Session {
userId: string;
token: string;
expiresAt: number;
}

class SessionStore {
constructor(private db: Database) {}

async create(userId: string, token: string, ttlMs: number): Promise<void> {
const session: Session = {
userId,
token,
expiresAt: Date.now() + ttlMs
};

await this.db.put(
Buffer.from(`sessions/${token}`),
Buffer.from(JSON.stringify(session))
);
}

async get(token: string): Promise<Session | null> {
const value = await this.db.get(Buffer.from(`sessions/${token}`));
if (!value) return null;

const session: Session = JSON.parse(value.toString());

// Check expiration
if (Date.now() > session.expiresAt) {
await this.delete(token);
return null;
}

return session;
}

async delete(token: string): Promise<void> {
await this.db.delete(Buffer.from(`sessions/${token}`));
}

async cleanup(): Promise<number> {
let removed = 0;
const now = Date.now();

for await (const [key, value] of this.db.scan(
Buffer.from('sessions/'),
Buffer.from('sessions;')
)) {
const session: Session = JSON.parse(value.toString());
if (now > session.expiresAt) {
await this.db.delete(key);
removed++;
}
}

return removed;
}
}

async function main() {
const db = await Database.open('./sessions');
const store = new SessionStore(db);

try {
// Create sessions
await store.create('user1', 'token123', 60000); // 1 minute
await store.create('user2', 'token456', 120000); // 2 minutes

console.log('Created 2 sessions');

// Retrieve session
const session = await store.get('token123');
console.log(`Session for ${session?.userId}: expires in ${Math.round((session!.expiresAt - Date.now()) / 1000)}s`);

// Cleanup expired
const removed = await store.cleanup();
console.log(`Cleaned up ${removed} expired sessions`);

} finally {
await db.close();
}
}

main().catch(console.error);

Output:

Created 2 sessions
Session for user1: expires in 60s
Cleaned up 0 expired sessions

API Reference

Database

MethodDescription
Database.open(path)Open/create database
put(key, value)Store key-value
get(key)Retrieve value (null if not found)
delete(key)Delete key
putPath(path, value)Store by path ⭐
getPath(path)Get by path ⭐
scan(start, end)Iterate range (async iterator) ⭐
beginTransaction()Begin transaction
query(prefix)Create query builder
checkpoint()Force checkpoint
close()Close database

Transaction

MethodDescription
put(key, value)Store in transaction
get(key)Retrieve from transaction
delete(key)Delete in transaction
scan(start, end)Scan in transaction
commit()Commit changes
abort()Rollback changes

IpcClient

MethodDescription
IpcClient.connect(opts)Connect to server
ping()Check latency
put(key, value)Store key-value
get(key)Retrieve value
scan(prefix)Scan prefix

Migration from 0.2.5

Path Operations

// ❌ 0.2.5: Incorrect wire format
await db.putPath('users/alice', value);
// May have produced incorrect keys

// ✅ 0.2.6: Fixed wire format
await db.putPath('users/alice', value);
// Now correctly encodes path segments

Scan Range

// ❌ 0.2.5: Manual range calculation
const start = Buffer.from('users/');
const end = Buffer.from('users/' + '\xFF'.repeat(100));

// ✅ 0.2.6: Simple semicolon trick
const start = Buffer.from('users/');
const end = Buffer.from('users;'); // ';' is after '/' in ASCII

Resources


Last updated: January 2026 (v0.5.1)