TypeScript — @lbb/client
@lbb/client is a thin, dependency-free wrapper over the platform fetch.
Request and response types are generated from little big brain’s API contract, so
every method is fully typed.
Runs anywhere there’s a global fetch: Node 18+, browsers, and edge workers — or
pass your own fetch via the fetch option.
Install
Section titled “Install”npm install @lbb/clientCreate a client
Section titled “Create a client”import { LbbClient, LbbError } from "@lbb/client";
const lbb = new LbbClient({ baseUrl: "https://db.eu.littlebigbrain.com", apiKey: process.env.LBB_API_KEY, // lbb_sk_live_… / lbb_sk_test_…});Write facts
Section titled “Write facts”Scope writes with client.graph("name"). Pass an idempotencyKey so retries
are safe:
await lbb.graph("main").facts.create( { triplets: [ { source: { type: "SERVICE", name: "auth-service" }, relation: "WRITES_TO", target: { type: "DATABASE", name: "user-db" }, confidence: 0.93, evidence: "auth-service writes identity records to user-db", }, ], }, { idempotencyKey: "import-2026-06-13" },);Build indexes
Section titled “Build indexes”await lbb.indexes.run({ wait: true }); // build BM25 + vector + adjacency, then waitOther lifecycle methods: indexes.build, indexes.delta, indexes.gc, and
compact.
Search
Section titled “Search”const results = await lbb.search.hybrid( "which systems store customer identity data", { topK: 5, source: "persisted", // or "ephemeral" consistency: "strong", // overlay the WAL tail on top of the index run targets: ["entities", "assertions"], },);
for (const assertion of results.assertions ?? []) { console.log(assertion.relation?.name, assertion.score);}Search family: search.hybrid, search.multi (RRF fusion of several
subqueries), search.fullText (BM25), and search.vector.
Traversal, temporal reads & lineage
Section titled “Traversal, temporal reads & lineage”// Bounded neighborhood traversal.await lbb.traverse({ entity: { entity_type: "SERVICE", name: "auth-service" }, relation: "WRITES_TO" });
// Text-seeded semantic traversal.await lbb.semanticTraverse({ query: "identity storage", maxHops: 2 });
// Temporal + provenance.await lbb.currentState({ entity: { entity_type: "DATABASE", name: "user-db" } });await lbb.history({ entity: { entity_type: "SERVICE", name: "auth-service" } });await lbb.why({ /* which observations support this assertion */ });await lbb.shacl({ /* SHACL-style shape query */ });SPARQL
Section titled “SPARQL”sparqlRows(...) runs a SPARQL 1.1 text query (SELECT or ASK) and returns
parsed results — no manual JSON.parse of a results string or zipping
head.vars with binding values:
const { vars, rows } = await lbb.sparqlRows({ query: `SELECT ?service ?db WHERE { ?service <https://littlebigbrain.com/r/writes_to> ?db } LIMIT 10`, reason: true, // optional: fold in rule-derived edges});for (const row of rows) console.log(row.service, "->", row.db);
const exists = (await lbb.sparqlRows({ query: "ASK { ?s ?p ?o }" })).boolean;sparqlRows returns { vars, boolean, bindings, rows }: rows flattens the
bindings to { variable: lexicalValue }, bindings keeps the raw typed term
objects, and boolean is the ASK answer (null for SELECT). sparqlText(...)
returns the unparsed envelope; the standalone parseSparqlResults(response)
helper parses it. For the structured BGP form, use sparql(body)
(SparqlSelectRequest).
Typed attribute filters without RDF IRIs
Section titled “Typed attribute filters without RDF IRIs”If you already have relation patterns and just need typed attribute predicates,
entities.filterByAttributes(...) builds the structured SPARQL filter body for
you:
await lbb.entities.filterByAttributes({ patterns: [{ subject: { var: "service" }, predicate: "WRITES_TO", object: { var: "db" } }], where: [ { field: "slo", op: "ge", value: 0.99 }, { var: "db", field: "tier", value: "prod" }, ], select: ["service"],});Response metadata & errors
Section titled “Response metadata & errors”Regular methods return parsed JSON. Use rawRequest(...) when you need
requestId, version, or headers. Every method throws LbbError on a non-2xx
response:
try { await lbb.graph("main").facts.create({ triplets: [/* … */] });} catch (err) { if (err instanceof LbbError) { console.error(err.status, err.code, err.message, err.param, err.requestId, err.docUrl); }}Method map
Section titled “Method map”| Area | Methods |
|---|---|
| Write | graph("main").facts.create |
| Search | search.hybrid, search.multi, search.fullText, search.vector |
| Search feedback | searchFeedback (grade 3/1/0 → qrels), searchFeedbackExport |
| Traversal | traverse, semanticTraverse |
| Temporal / lineage / shapes | currentState, history, why, shacl |
| SPARQL | sparqlRows, sparql, sparqlText, analytics |
| Ontology | ontologyView ({ counts: true } for edge counts), ontologySearch, ontologyResolve, ontologyDefine |
| Index lifecycle | indexes.run, indexes.build, indexes.delta, indexes.gc, compact |
| Inspection | entities.list, entities.filterByAttributes, status, metadata, summary |
| Schema | schema.view, schema.preview, schema.publish, schema.audit |
| Admin | adminCreateStack, adminStack, adminRotateStackKey, adminDeleteStack |
Request/response shapes are exported as Schemas["TypeName"] (e.g.
Schemas["SemanticGraphSearchRequest"]); the raw generated components,
paths, and operations are exported too. See
Types & generated models for how the types are generated and
a cross-reference of the common type names.
Native SPARQL Protocol
Section titled “Native SPARQL Protocol”A standalone stack also serves the native SPARQL 1.1 Protocol at /sparql
(GET ?query=, POST form or application/sparql-query body,
Accept-negotiated JSON/XML/CSV/TSV) for off-the-shelf clients like YASGUI and
Protégé. sparqlRows is the in-process equivalent that returns parsed JSON.