batenbase
v2.9.7
Published
Official BatenBase SDK — causal storage for builders
Downloads
580
Maintainers
Readme
batenbase
Official JavaScript / TypeScript SDK for BatenBase
BatenBase is a causal key-value database — every block automatically records when it was created, when it was last written, who can see it, and whether it has been redacted. You get a full audit log for free, with no schema to define and no migrations to run.
If you have ever built a system where you needed to answer "what happened to this record and when?" — BatenBase was designed for exactly that.
npm install batenbaseHow it works
Every piece of data you store is called a block. A block has a key, a payload (data), and four causal fields that BatenBase manages for you:
| Field | Type | Meaning |
|-------|------|---------|
| f | number | First write — creation timestamp in Unix ms. Immutable. |
| l | number | Last write — updated automatically on every set(). |
| v | number | Visibility scope — controls who can read this block. |
| h | number | Hidden flag — 0 = visible, 1 = redacted. |
You write { name: "Alice" }. You get back { key, f, l, v, h, data: { name: "Alice" } }. No configuration required.
Quick start
import { BatenBase } from "batenbase"
const db = new BatenBase({ token: "YOUR_TOKEN" })
// Store anything — users, invoices, sessions, contracts
await db.set("user:[email protected]", {
name: "Alice",
role: "admin",
plan: "pro"
})
// Read it back — f and l are set automatically
const block = await db.get("user:[email protected]")
console.log(block.data.name) // "Alice"
console.log(block.f) // e.g. 1748700000000 ← created at
console.log(block.l) // e.g. 1748700000000 ← last modified
// Scan by prefix — all users, all invoices, all sessions
const users = await db.list({ prefix: "user:", limit: 200 })
// Append an immutable audit event
await db.log("user.login", { email: "[email protected]", ip: "1.2.3.4" })
// Query the audit trail
const recent = await db.events({ since: "24h", limit: 100 })Get your token from dashboard.batenbase.com.
Constructor
const db = new BatenBase({
token: "YOUR_TOKEN", // required
baseUrl: "https://api.batenbase.com" // optional override
})Methods
set(key, data) → { ok, key }
Upsert a block. On the first write, f is set and never changes. On every subsequent write, l is updated.
await db.set("invoice:2026-001", {
client: "Acme Corp",
amount: 9500,
status: "pending"
})get(key) → Block
Read one block. Returns the full causal envelope.
const inv = await db.get("invoice:2026-001")
// inv.f → creation timestamp
// inv.l → last modified
// inv.data.status → "pending"delete(key) → { ok }
Permanently delete a block.
await db.delete("session:expired-token")list({ prefix?, limit? }) → Block[]
Prefix scan. Max 1000 results per call. Use type: prefixes to scope your scans efficiently.
const invoices = await db.list({ prefix: "invoice:", limit: 500 })
const sessions = await db.list({ prefix: "session:" })log(kind, details?) → { ok }
Append an immutable event to the audit log. kind is a dot-separated string that describes the event type. Events cannot be deleted or modified.
await db.log("invoice.paid", { id: "2026-001", amount: 9500 })
await db.log("user.suspended", { email: "[email protected]", reason: "abuse" })
await db.log("contract.signed",{ parties: ["alice", "bob"], hash: "abc123" })events({ since?, limit? }) → Event[]
Query the audit log. since accepts "1h", "24h", "7d", "30d", or a Unix timestamp in ms.
const today = await db.events({ since: "24h" })
const week = await db.events({ since: "7d", limit: 1000 })
const from = await db.events({ since: 1748700000000 })me() → Tenant
Returns your tenant info: plan, blocks used, and quota.
const info = await db.me()
// { plan: "pro", blocks_used: 4821, quota: 1000000 }health() → { status }
Liveness check. No token required. Useful for monitoring.
const { status } = await db.health()
// { status: "ok" }Key convention
Keys are arbitrary UTF-8 strings. The recommended pattern is type:identifier — this makes prefix scans fast and your data self-organizing.
user:[email protected]
invoice:2026-001
session:a1b2c3d4e5f6
contract:client-42:v3
log:2026-01-15:server-01You can nest prefixes (contract:client-42:v3) for hierarchical data without any special configuration.
Error handling
All API errors throw a BatenBaseError with a status code.
import { BatenBase, BatenBaseError } from "batenbase"
try {
const block = await db.get("user:unknown")
} catch (e) {
if (e instanceof BatenBaseError) {
console.log(e.status) // 404
console.log(e.message) // "block not found"
}
}| Status | Code | Meaning |
|--------|------|---------|
| 401 | unauthorized | Invalid or missing token |
| 404 | not_found | Block does not exist |
| 429 | rate_limited | Too many requests |
| 507 | quota_exceeded | Block limit reached — upgrade your plan |
| 500 | internal_error | Server error |
TypeScript
The SDK is written in TypeScript and ships with full type definitions. No @types package needed.
import { BatenBase, Block, BatenBaseError } from "batenbase"
const db = new BatenBase({ token: process.env.BATENBASE_TOKEN! })
async function getUser(email: string): Promise<Block> {
return db.get(`user:${email}`)
}Links
- BatenBase — product overview
- Documentation — from first principles to architecture
- REST Reference — full endpoint list
- Dashboard — manage tokens and quota
