Skip to content

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.

Terminal window
npm install @lbb/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_…
});

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" },
);
await lbb.indexes.run({ wait: true }); // build BM25 + vector + adjacency, then wait

Other lifecycle methods: indexes.build, indexes.delta, indexes.gc, and compact.

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.

// 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 */ });

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

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"],
});

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);
}
}
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.

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.