JavaScript / TypeScript SDK
Build SapixDB-powered apps in Node.js, Bun, Deno, or the browser. Zero dependencies — uses the platform fetch API. Full TypeScript support with generics.
Installation
npm install sapixdb
bun add sapixdb
yarn add sapixdb
Quick Start
import { SapixClient } from "sapixdb";
const db = new SapixClient({
url: "http://localhost:7475", // your SapixDB agent
agent: "my-app", // matches SAPIX_AGENT_ID
});
// ✅ Check connection
const alive = await db.ping(); // true / false, never throws
// ✍️ Write a record
const product = await db.collection("products").write({
name: "Classic T-Shirt",
price: 29.99,
stock: 100,
});
console.log(product.id); // "nuc_abc123"
console.log(product.hash); // "sha3:e7f2a1..." (cryptographic proof)
// 📖 Read latest records
const all = await db.collection("products").latest();
// 🔍 Filter
const shirts = await db.collection("products").find({ category: "apparel" });
// ⏱️ Time travel
const yesterday = await db
.collection("orders")
.asOf("2026-05-11T00:00:00Z")
.latest();TypeScript Generics
Pass your own interface to collection<T>() for full autocomplete and type safety on every read and write.
interface Product {
name: string;
price: number;
stock: number;
category?: string;
}
const products = db.collection<Product>("products");
// write() only accepts Partial<Product> — wrong fields fail at compile time
await products.write({ name: "Sneakers", price: 89.99, stock: 50 });
// latest() returns NucleotideRecord<Product>[]
const items = await products.latest();
const price = items[0].data.price; // typed as number ✓Collection API
Every read and write goes through db.collection(name). Collections are schema-free — you never declare them; they emerge from your first write.
.write(data)→ Promise<WriteResult>WriteResult contains id, hash, prev_hash, and timestamp..writeBatch(records[])→ Promise<WriteResult[]>.write() for each item..get(id)→ Promise<NucleotideRecord>SapixNotFoundError if not found..latest(options?)→ Promise<NucleotideRecord[]>.history(options?)→ Promise<NucleotideRecord[]>.find(filter, options?)→ Promise<NucleotideRecord[]>.findOne(filter)→ Promise<NucleotideRecord | null>null. Never throws on empty result..asOf(timestamp)→ CollectionQueryCollectionQuerywith the same .latest(), .find(), .findOne(), and .all() methods.Time Travel Queries
// What was the order status 30 minutes ago?
const thirtyMinAgo = new Date(Date.now() - 30 * 60_000).toISOString();
const snapshot = await db
.collection("orders")
.asOf(thirtyMinAgo)
.find({ customer_id: "cust_001" });
// The result shows the order as it was 30 minutes ago —
// before any status updates that happened since.Graph Relationships
Connect records with typed directed edges and traverse the graph to any depth. Useful for org charts, order→customer links, product→category trees, and access control graphs.
db.graph.relate(src, dst, type, weight?)→ Promise<void>db.graph.traverse(fromId, options?)→ Promise<TraverseResult>{ nodes, edges }. Options: depth (default 1), direction — "outbound" | "inbound" | "both".db.graph.neighbors(id, direction?)→ Promise<NucleotideRecord[]>// Link records
await db.graph.relate(order.id, customer.id, "placed_by");
await db.graph.relate(product.id, category.id, "belongs_to");
// Traverse: find all orders placed by this customer (depth 2)
const { nodes, edges } = await db.graph.traverse(customer.id, {
depth: 2,
direction: "inbound",
});Agent Ingest
Use db.ingest() for automated pipelines — AI agents, webhooks, cron jobs. Identical to .write() but routes through the dedicated ingest endpoint, which supports optional dual-write to Supabase.
// Log every AI decision permanently and immutably
await db.ingest("ai_decisions", {
model: "gpt-4o",
action: "approve_loan",
confidence: 0.94,
applicant: "cust_001",
reasoning: "Credit score 780, DTI 28%",
});Error Handling
import { SapixError, SapixNetworkError, SapixNotFoundError } from "sapixdb";
try {
const record = await db.collection("orders").get("nuc_missing");
} catch (err) {
if (err instanceof SapixNotFoundError) {
// 404 — record does not exist
} else if (err instanceof SapixNetworkError) {
// Cannot reach SapixDB — check if it's running
} else if (err instanceof SapixError) {
console.log(err.status); // HTTP status code
console.log(err.message); // Error message from the server
}
}Full Example: Online Store
import { SapixClient } from "sapixdb";
const db = new SapixClient({ url: "http://localhost:7475", agent: "store" });
async function main() {
// 1. Add products
const shirt = await db.collection("products").write({
sku: "SHIRT-001", name: "Classic T-Shirt",
price: 29.99, stock: 200, category: "apparel",
});
// 2. Register customer
const customer = await db.collection("customers").write({
name: "Alice Johnson", email: "[email protected]",
});
// 3. Place order
const order = await db.collection("orders").write({
customer_id: customer.id,
items: [{ product_id: shirt.id, qty: 2, unit_price: 29.99 }],
total: 59.98,
status: "placed",
});
// 4. Link in graph
await db.graph.relate(order.id, customer.id, "placed_by");
await db.graph.relate(order.id, shirt.id, "contains");
// 5. Ship (append — the "placed" version is preserved forever)
await db.collection("orders").write({
customer_id: customer.id,
status: "shipped",
tracking: "UPS-1Z999AA10123456784",
shipped_at: new Date().toISOString(),
});
// 6. Audit: what did the order look like when it was placed?
const placedAt = order.timestamp;
const [original] = await db
.collection("orders")
.asOf(placedAt)
.find({ customer_id: customer.id });
console.log(original.data.status); // "placed" — not "shipped"
}
main();SDKs for Python, Go, and Rust are on the roadmap. Join the early access list to get notified and shape the API design.
Join Early Access →