SapixDBSapixDB/Docs
Early Access
JavaScript & TypeScript SDK · v0.1

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
npm install sapixdb
bun
bun add sapixdb
yarn
yarn add sapixdb

Quick Start

TypeScript
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.

TypeScript
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>
Append a new record. Every call creates a new nucleotide — nothing is ever overwritten. The returned WriteResult contains id, hash, prev_hash, and timestamp.
.writeBatch(records[])Promise<WriteResult[]>
Write multiple records in parallel. Same semantics as .write() for each item.
.get(id)Promise<NucleotideRecord>
Fetch one record by its nucleotide ID. Throws SapixNotFoundError if not found.
.latest(options?)Promise<NucleotideRecord[]>
Fetch the current (most recent) version of every record in the collection.
.history(options?)Promise<NucleotideRecord[]>
Fetch the full append-only history — every version ever written.
.find(filter, options?)Promise<NucleotideRecord[]>
Find records matching a key/value filter (latest version only).
.findOne(filter)Promise<NucleotideRecord | null>
Returns the first matching record, or null. Never throws on empty result.
.asOf(timestamp)CollectionQuery
Scope all subsequent reads to a point in time. Returns a CollectionQuerywith the same .latest(), .find(), .findOne(), and .all() methods.

Time Travel Queries

TypeScript
// 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>
The simplest way to create a relationship between two records.
db.graph.traverse(fromId, options?)Promise<TraverseResult>
Walk the graph from a starting node. Returns { nodes, edges }. Options: depth (default 1), direction"outbound" | "inbound" | "both".
db.graph.neighbors(id, direction?)Promise<NucleotideRecord[]>
Get direct neighbours of a node (depth 1 shortcut).
TypeScript
// 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.

TypeScript
// 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

TypeScript
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

TypeScript — store.ts
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();
Python SDK coming soon

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 →