Basic Usage
A complete walkthrough of GraphDB features using a user management scenario. Every snippet below is self-contained and runnable.
Setup
import { GraphDB } from "@graphdb/core";
interface User { name: string; email: string; role: "admin" | "editor" | "viewer"; age: number; active: boolean;}
const db = GraphDB();
const users = db.createCollection<User>("users", { indexes: ["email", "role", "active"],});Indexes on email, role, and active speed up equality and in lookups on those fields. Other operators still work on any field — they just fall back to a full scan.
Creating documents
create is async and returns the generated _id.
const aliceId = await users.create({ name: "Alice Johnson", email: "alice@example.com", role: "admin", age: 34, active: true,});// aliceId -> "a1b2c3d4-..." (crypto.randomUUID)
const bobId = await users.create({ name: "Bob Smith", email: "bob@example.com", role: "editor", age: 28, active: true,});
const carolId = await users.create({ name: "Carol White", email: "carol@example.com", role: "viewer", age: 41, active: false,});
const daveId = await users.create({ name: "Dave Park", email: "dave@example.com", role: "editor", age: 25, active: true,});Every document stored in the collection is wrapped in Doc<User>, which adds _id, createdAt, and updatedAt (epoch milliseconds).
Reading a single document
read is synchronous and returns Doc<User> | null.
const alice = users.read(aliceId);// {// _id: "a1b2c3d4-...",// createdAt: 1739712000000,// updatedAt: 1739712000000,// name: "Alice Johnson",// email: "alice@example.com",// role: "admin",// age: 34,// active: true,// }
const ghost = users.read("nonexistent-id");// nullQuerying with where clauses
Primitive equality
The simplest filter is a plain value match.
const admins = users.query({ role: "admin" });// [ Doc<User> for Alice ]Comparison operators
const over30 = users.query({ age: { gt: 30 } });// [ Alice (34), Carol (41) ]
const under35 = users.query({ age: { lt: 35 } });// [ Bob (28), Alice (34), Dave (25) ]
const exactly28 = users.query({ age: { eq: 28 } });// [ Bob ]String operators
const startsWithD = users.query({ name: { startsWith: "D" } });// [ Dave Park ]
const gmailUsers = users.query({ email: { endsWith: "@example.com" } });// [ Alice, Bob, Carol, Dave ]
const containsSmith = users.query({ name: { includes: "Smith" } });// [ Bob Smith ]RegExp matching
You can pass a RegExp directly as a top-level where value, or use the match operator.
// Top-level RegExpconst matchJ = users.query({ name: /johnson/i });// [ Alice Johnson ]
// match operatorconst matchPark = users.query({ name: { match: /park$/i } });// [ Dave Park ]The in operator
const editorsAndViewers = users.query({ role: { in: ["editor", "viewer"] } });// [ Bob, Carol, Dave ]Combining multiple fields
All fields in a where clause are ANDed together.
const activeEditors = users.query({ role: "editor", active: true });// [ Bob, Dave ]Query options: sorting, skip, and limit
// Sort by age descendingconst byAge = users.query({}, { orderBy: { age: "DESC" } });// [ Carol (41), Alice (34), Bob (28), Dave (25) ]
// Pagination: page 1, 2 items per pageconst page1 = users.query( {}, { orderBy: { name: "ASC" }, limit: 2, skip: 0 },);// [ Alice Johnson, Bob Smith ]
// Page 2const page2 = users.query( {}, { orderBy: { name: "ASC" }, limit: 2, skip: 2 },);// [ Carol White, Dave Park ]findOne
Returns the first matching document or null. Useful when you expect exactly one result.
const admin = users.findOne({ role: "admin" });// Doc<User> for Alice
const missing = users.findOne({ role: "superadmin" });// nullcount and exists
const totalUsers = users.count();// 4
const activeCount = users.count({ active: true });// 3
const aliceExists = users.exists(aliceId);// true
const ghostExists = users.exists("nonexistent-id");// falseUpdating a document
update is async and returns the full updated document.
const updatedCarol = await users.update(carolId, { active: true, age: 42 });// {// _id: "...",// createdAt: 1739712000000,// updatedAt: 1739712060000, <-- updated// name: "Carol White",// email: "carol@example.com",// role: "viewer",// age: 42, <-- changed// active: true, <-- changed// }Only the fields you pass in the patch are changed. updatedAt is bumped automatically.
Bulk operations
updateMany
await users.updateMany({ role: "editor" }, { active: false });// Bob and Dave are now inactiveremoveMany
const result = await users.removeMany({ active: false });// Removes Bob and DaveRemoving a document
const removeResult = await users.remove(aliceId);// { removedId: "a1b2c3d4-...", acknowledge: true }Listening to events
Collection-level events
const unsubCreate = users.on("create", ({ doc }) => { console.log("New user:", doc.name);});
const unsubUpdate = users.on("update", ({ before, after, patch }) => { console.log(`Updated ${before.name}:`, patch);});
const unsubRemove = users.on("remove", ({ doc }) => { console.log("Removed user:", doc.name);});
// Trigger the listenersawait users.create({ name: "Eve Turner", email: "eve@example.com", role: "viewer", age: 30, active: true,});// Console: "New user: Eve Turner"
// Unsubscribe when doneunsubCreate();unsubUpdate();unsubRemove();Per-document listeners
const eveId = (await users.findOne({ name: /eve/i }))?._id;
if (eveId) { const cancel = users.listen(eveId, (doc) => { console.log("Eve changed:", doc); });
await users.update(eveId, { role: "editor" }); // Console: "Eve changed: { _id: ..., name: 'Eve Turner', role: 'editor', ... }"
cancel(); // stop listening}Populating initial data
populate inserts many documents at once and fires a single populate event instead of individual create events.
users.on("populate", ({ count }) => { console.log(`Loaded ${count} users`);});
await users.populate([ { name: "Frank Lee", email: "frank@example.com", role: "viewer", age: 22, active: true, }, { name: "Grace Kim", email: "grace@example.com", role: "admin", age: 37, active: true, },]);// Console: "Loaded 2 users"Clearing a collection
await users.clear();
const afterClear = users.count();// 0Retrieving a collection later
If another part of your application needs access to an existing collection, use getCollection.
const usersAgain = db.getCollection<User>("users");// Collection<User> | nullThis returns the same collection instance — no data is duplicated.