TypeScript client SDK for LivenDB
import { LivenClient, Pipeline, Filter } from "@livendb/liven-client";
const client = await LivenClient.connect("127.0.0.1:43121");
await client.insert("users", "u1", { name: "Alice", role: "admin" });
const user = await client.get("users", "u1");
console.log(user);
// → [{ stream_name: "users", key: "u1", value: { name: "Alice", role: "admin" }, … }]
const results = await client.run(
Pipeline.from("users")
.filter(Filter.field("role").eq("admin"))
.limit(10)
);
client.close();npm install @livendb/liven-clientRequires Node.js 18+.
import { LivenClient } from "@livendb/liven-client";
// Default port is 43121
const client = await LivenClient.connect("localhost");
// With authentication key
const client = await LivenClient.connect("localhost:43121?auth_key=your-api-key");// Insert
await client.insert("events", "e1", { type: "click", value: 42 });
// Upsert (insert or replace)
await client.upsert("events", "e1", { type: "click", value: 99 });
// Update (merge with existing)
await client.update("users", "u1", { last_login: Date.now() });
// Get by key
const record = await client.get("users", "u1");
// Delete
await client.delete("events", "e1");
// Clear all records from a stream
await client.clear("sessions");
// Drop an entire stream
await client.dropStream("old_data");await client.insertMany("events", [
["e1", { type: "click" }],
["e2", { type: "view" }],
["e3", { type: "purchase" }],
]);
await client.upsertMany("users", [
["u1", { name: "Alice", score: 100 }],
["u2", { name: "Bob", score: 85 }],
]);import { Pipeline, Filter } from "@livendb/liven-client";
const results = await client.run(
Pipeline.from("orders")
.filter(Filter.field("amount").gte(100))
.filter(Filter.field("status").eq("completed"))
.sort("amount", true)
.limit(10)
);// Filter
await client.filter("events", Filter.field("type").eq("click"));
// Limit
await client.limit("events", 50);
// Count
const [countResult] = await client.count("events");
// Sort
await client.sort("orders", "total", true); // descending
// Pagination
await client.page("events", 1, 25); // page 1, 25 per page
await client.pageCursor("feed", "cursor_abc", 25); // cursor-based
// Field projection
await client.map("users", ["name", "email"]);
// Time-windowed aggregation
await client.window("pageviews", 60000, "count"); // 1min window
await client.window("sales", 3600000, "sum"); // 1hr sum
await client.window("sensors", 300000, "avg"); // 5min avg
// Group by
await client.group("events", "type", ["count", "sum(value)"]);
// Distinct
await client.distinct("visitors", "ip_address");
// Vector similarity search
await client.vectorFilter("documents", "embedding", [1, 0, -1, …], 0.75);// Enrich (left join)
await client.enrich("orders", "users", "user_id");
// Correlate (time-bounded join)
await client.correlate("events", "sessions", "session_id", 5000);
// Chain (multi-hop join)
await client.chain("reviews", "orders", "order_id");
// Sequence (event pattern matching)
await client.sequence(
"events",
[
Filter.field("type").eq("login"),
Filter.field("type").eq("purchase"),
],
300000 // within 5 minutes
);// Update records matching a pipeline
await client.runUpdate(
Pipeline.from("events").filter(Filter.field("status").eq("pending")),
{ status: "processed" }
);
// Delete records matching a pipeline
await client.runDelete(
Pipeline.from("events").filter(Filter.field("type").eq("temp"))
);// Subscribe to matching records in real-time
await client.runListen(
Pipeline.from("alerts").filter(Filter.field("priority").eq("critical"))
);// List all streams
const streams = await client.streams();
// Server status
const status = await client.status();| Method | Description |
|---|---|
LivenClient.connect(addr) |
Connect to a Liven server. Supports ?auth_key= in URL |
close() |
Close the connection |
query(dsl) |
Execute a raw LIVEN DSL string |
insert(stream, key, value) |
Insert a record |
upsert(stream, key, value) |
Insert or replace a record |
update(stream, key, value) |
Merge value into existing record |
delete(stream, key) |
Delete a record by key |
get(stream, key) |
Get a record by key |
clear(stream) |
Remove all records from a stream |
dropStream(stream) |
Remove an entire stream |
insertMany(stream, batch) |
Batch insert |
upsertMany(stream, batch) |
Batch upsert |
streams() |
List all streams |
status() |
Server status |
filter(stream, filter) |
Query with filter |
limit(stream, count) |
Query with limit |
count(stream) |
Count records |
sort(stream, field, descending) |
Query with sort |
page(stream, page, size) |
Page-based pagination |
pageCursor(stream, cursor, size) |
Cursor-based pagination |
map(stream, fields) |
Field projection |
window(stream, durationMs, strategy) |
Time-windowed aggregation |
group(stream, field, aggregations) |
Group-by aggregation |
distinct(stream, field) |
Distinct values |
vectorFilter(stream, field, vector, threshold) |
Vector similarity search |
enrich(stream, source, joinKey) |
Left join |
correlate(stream, source, joinKey, withinMs) |
Time-bounded join |
chain(stream, target, joinKey) |
Multi-hop join |
sequence(stream, steps, withinMs) |
Event pattern matching |
run(pipeline) |
Execute a Pipeline builder |
runListen(pipeline) |
Subscribe to pipeline results |
runUpdate(pipeline, value) |
Update matching records |
runDelete(pipeline) |
Delete matching records |
Builder for constructing pipeline queries.
Pipeline.from("stream")
.filter(filter)
.get(key)
.map(["field1", "field2"])
.window(5000, "count")
.limit(50)
.sort("field", true)
.page(1, 20)
.pageCursor("cursor", 25)
.count()
.group("field", ["count", "sum(x)"])
.correlate("source", "key", 5000)
.chain("target", "key")
.distinct("field")
.enrich("source", "key")
.vectorFilter("embedding", [1, 2, 3], 0.8)
.sequence([filter1, filter2], 300000)
.build() // → DSL string
.buildListen() // → DSL string + .listen()
.buildUpdate(value) // → DSL string + update(value)
.buildDelete() // → DSL string + delete()Builder for typed filter expressions.
| Method | Description |
|---|---|
Filter.field(name) |
Start building a filter for a field |
Filter.and([...filters]) |
Combine filters with AND |
Filter.or([...filters]) |
Combine filters with OR |
Filter.not(filter) |
Negate a filter |
Field comparisons:
Filter.field("status").eq("active")
Filter.field("status").ne("deleted")
Filter.field("amount").gt(100)
Filter.field("amount").lt(50)
Filter.field("amount").gte(100)
Filter.field("amount").lte(50)
Filter.field("name").contains("alice")
Filter.field("name").startsWith("al")
Filter.field("name").endsWith("ice")
Filter.field("amount").between(10, 100)
Filter.field("status").in(["active", "pending"])Compound filters:
Filter.and([
Filter.field("status").eq("active"),
Filter.field("age").gte(18),
])
Filter.or([
Filter.field("role").eq("admin"),
Filter.field("role").eq("moderator"),
])
Filter.not(Filter.field("status").eq("deleted"))The client communicates with LivenDB over TCP using framed msgpack messages:
[version: u8][length: u32 BE][discriminator: u8][msgpack_payload]
- version:
0x01 - discriminator:
0x02(msgpack),0x03(raw vector bytes) - length: payload size in bytes (big-endian)
Legacy v0 frames (4-byte length prefix, no version/discriminator) are supported for decoding.
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test
# Watch mode
npm run test:watchMIT