TypeScript client
mushroomdb-client is a typed wrapper over the HTTP API and its WebSocket stream.
It talks to a running mushroomdb serve; it does not embed the engine.
npm install mushroomdb-client
It needs Node 18 or later, and runs in a browser. In a Node without a global WebSocket,
install ws as well if you use subscriptions.
Quick start
import { MushroomClient } from 'mushroomdb-client';
const client = new MushroomClient('http://127.0.0.1:8080', { token: 'your-token' });
// A read.
const result = await client.query(
'MATCH (p:Person) RETURN p.id AS id, p.name AS name LIMIT 10',
);
console.log(result.columns); // ['id', 'name']
console.log(result.rows); // [['person-01', 'Alice'], ...]
// A read with a parameter.
const one = await client.query(
'MATCH (p:Person {id: $pid}) RETURN p.name AS name',
{ params: { pid: 'person-01' } },
);
// Why are these two linked?
const why = await client.explain('person-01', 'proj-01');
The token is sent as an Authorization: Bearer header, and on the WebSocket URL as ?token=
for subscriptions. Leave the second argument out for a server started without one.
Methods
Each method calls one route. The route’s page has the request and the response in full.
| Call | What it does | Route |
|---|---|---|
client.query( | Run a Cypher read or write | POST /query |
client.ingest( | Insert many nodes of one label | POST /ingest |
client.stats( | Node, edge and rule counts | GET /stats |
client.suggest( | Rules the store proposes from its own values | GET /suggest |
client.explain( | Why two nodes are linked | GET /explain |
client.createRule( | Declare a rule | POST /rules |
client.node( | One node’s label and properties; null when it does not exist | GET /node/{key} |
client.neighborhood( | What is within a few hops of a node | GET /node/{key}/neighborhood |
client.algo( | PageRank, connected components or degree, by name | POST /algo/pagerank, /wcc, /degree |
client.renameNode( | Change a node’s key | POST /nodes/{key}/rename |
client.upsertEdge( | Create an edge, and its endpoints if they are missing | POST /edges/upsert |
client.subscribe( | Stream rule and write events | GET /subscribe |
client.health( | Server liveness and node/edge counts (when authenticated) | GET /health |
client.metrics( | Detailed runtime metrics including slow-query log | GET /metrics |
client.findSimilar( | Approximate nearest-neighbour vector search | POST /find_similar |
client.createNode( | Insert a node by label and key | POST /nodes |
client.deleteNode( | Remove a node; throws when it does not exist | DELETE /node/{key} |
client.nodeEdges( | All edges attached to a node | GET /node/{key}/edges |
client.nodeHistory( | Per-commit change log for a node | GET /node/{key}/history |
client.edgeHistory( | Edge presence log between two nodes | GET /history/edge |
client.wasLinked( | Whether two nodes were linked at a given commit | GET /history/was_linked |
client.createEdge( | Insert an explicit (non-derived) edge | POST /edges |
client.deleteEdge( | Remove an explicit edge | DELETE /edges/{etype}/{src}/{dst} |
client.setNodeProp( | Set or overwrite one property on a node | PUT /node/{key}/prop/{field} |
client.removeNodeProp( | Delete one property from a node | DELETE /node/{key}/prop/{field} |
client.backup( | Copy the store to a directory and verify it | POST /backup |
client.watch( | Stream the raw mutation log over WebSocket | GET /watch |
What it does not wrap
All HTTP routes and WebSocket endpoints have a method on the client.
Creating a node, setting a property and deleting can also be done through client.query, with
a Cypher CREATE, SET or DELETE.
Query options
client.query(cypher, opts?) accepts three optional fields beyond params:
| Option | Type | What it does |
|---|---|---|
as_of | number | Time-travel: run the query on the graph as it was at this WAL commit index (0 to commit_seq − 1, read-only). A commit index from a nodeHistory/edgeHistory entry is the usual source. |
mask | string[] | Node-key allow-list: the query sees only nodes whose key is in this set. Makes the query read-only. |
namespace | string | Restrict results to this namespace, intersecting with mask and any role. Makes the query read-only. |
Errors
A response that is not a success throws MushroomError. Its detail, which is also its
message, is what the server said went wrong. Empty or plain-text error bodies (e.g. 404
with no JSON, or 413) surface as MushroomError("HTTP <status>") rather than a raw parse
error.
import { MushroomError } from 'mushroomdb-client';
try {
await client.query('BAD CYPHER');
} catch (err) {
if (err instanceof MushroomError) {
console.error('Query failed:', err.detail);
}
}
Subscriptions
const handle = await client.subscribe(
{ rules: ['skill_fit'], writes: true },
(ev) => {
switch (ev.type) {
case 'edge_fired':
console.log(`${ev.rule}: ${ev.src_key} -> ${ev.dst_key} (commit ${ev.commit_seq})`);
break;
case 'lagged':
console.warn(`missed ${ev.missed} events; re-read what you depend on`);
break;
}
},
);
// Later:
await handle.close();
- A subscription sees only writes made through the server it is connected to.
- It does not reconnect. When the connection drops, no more events arrive; subscribe again.
- A
laggedevent means the server’s queue for this subscriber overflowed and some events were not sent. The stream goes on. - In a Node without a global
WebSocket, pass thewsconstructor aswsConstructorin the first argument.
Every option of every method is in the package’s own README.