A type-safe, realtime collaborative Graph Database in a CRDT
Summary
Codemix open-sources @codemix/graph, a type-safe, CRDT-backed graph database with TypeScript-native schema validation and realtime offline-first sync via Yjs.
View Cached Full Text
Cached at: 04/21/26, 11:19 AM
@codemix/graph - codemix
Source: https://codemix.com/graph Plane route demo
Global airline routes demo
Load a snapshot of real airline routes into the graph and query it with TypeScript.
Live demo
Add your face to the wall
Powered by@codemix/graphand@codemix/y\-graph\-storage— a real graph database, synced via a Yjs CRDT across every open tab. Add yourself, rearrange people, draw connections.
Installation
Install the package from npm — no native dependencies, runs anywhere Node or a bundler can.
$ pnpm add @codemix/graph
**Note:**This is alpha-quality software. We use it in production at codemix and it works well for our use cases, but please be careful using it with your own data.
Define your schema
Describe vertices, edges, and indexes in a plain object. Property types flow through every query, traversal, and mutation — no casts, no runtime surprises.
import { Graph, GraphSchema, InMemoryGraphStorage } from "@codemix/graph";
import { z } from "zod";
const schema = {
vertices: {
User: {
properties: {
email: { type: z.email(), index: { type: "hash", unique: true } },
name: { type: z.string() },
},
},
Repo: {
properties: {
name: { type: z.string() },
stars: { type: z.number() },
},
},
},
edges: {
OWNS: { properties: {} },
FOLLOWS: { properties: {} },
},
} as const satisfies GraphSchema;
const graph = new Graph({ schema, storage: new InMemoryGraphStorage() });
- →Any Standard Schema library— Zod, Valibot, ArkType, or your own.
- →Validated on every mutation— properties are checked on
addVertex,addEdge, andupdateProperty. - →Indexes declared inline— hash, B-tree, and full-text; built lazily and maintained incrementally.
Add some data
Vertices and edges are added through the graph instance. Property arguments are checked against your schema at both compile time and runtime.
// add vertices — args are typed to each label's property schema
const alice = graph.addVertex("User", { name: "Alice", email: "[email protected]" });
const bob = graph.addVertex("User", { name: "Bob", email: "[email protected]" });
const myRepo = graph.addVertex("Repo", { name: "my-repo", stars: 0 });
// add edges
graph.addEdge(alice, "OWNS", myRepo, {});
graph.addEdge(bob, "FOLLOWS", alice, {});
// read properties — types come from the schema
alice.get("name"); // string
myRepo.get("stars"); // number
// update in place
graph.updateProperty(myRepo, "stars", 42);
// or via the element itself
myRepo.set("stars", 42);
Write type-safe queries
A Gremlin-style traversal API — familiar step names, but every label, property key, and hop is checked by TypeScript against your schema.
Start a traversal
import { GraphTraversal } from "@codemix/graph";
const g = new GraphTraversal(graph);
for (const path of g.V().hasLabel("User")) {
path.value.get("name"); // string ✓
path.value.get("email"); // string ✓
}
Filter by property
// exact match or predicate
const [alice] = g.V()
.hasLabel("User")
.has("email", "[email protected]");
const seniors = g.V()
.hasLabel("User")
.where((v) => v.get("name").startsWith("A"));
Traverse edges
// follow OWNS edges from User → Repo
for (const path of g.V()
.hasLabel("User")
.has("email", "[email protected]")
.out("OWNS").hasLabel("Repo")) {
path.value.get("stars"); // number — typed from Repo's schema
}
Label and select
// capture vertices at multiple hops and project them together
for (const { user, repo } of g.V()
.hasLabel("User").as("user")
.out("FOLLOWS")
.out("OWNS").hasLabel("Repo").as("repo")
.select("user", "repo")) {
console.log(
user.value.get("name"), // string
repo.value.get("stars"), // number
);
}
Offline-first sync and realtime collaboration
SwapInMemoryGraphStorageforYGraphand the entire graph lives in aYjsCRDT document. Every traversal, Cypher query, and index works unchanged — you just get conflict-free sync on top.
Plug in a provider
import * as Y from "yjs";
import { WebsocketProvider } from "y-websocket";
import { YGraph } from "@codemix/y-graph-storage";
const doc = new Y.Doc();
const graph = new YGraph({ schema, doc });
// Connect any Yjs provider — sync happens automatically.
// Every peer that joins the room sees the same graph.
const provider = new WebsocketProvider("wss://my-server", "graph-room", doc);
Subscribe to fine-grained changes
// Events fire for local and remote mutations alike
const unsubscribe = graph.subscribe({
next(change) {
// change.kind is one of:
// "vertex.added" | "vertex.deleted"
// "edge.added" | "edge.deleted"
// "vertex.property.set" | "vertex.property.changed"
console.log(change.kind, change.id);
},
});
Live queries
// Wraps any traversal and re-fires when the result set could change
const topRepos = graph.query((g) =>
g.V().hasLabel("Repo").order("stars", "desc").limit(10)
);
const unsubscribe = topRepos.subscribe({
next() {
for (const path of topRepos) {
console.log(path.value.get("name"), path.value.get("stars"));
}
},
});
// Adding or updating a Repo elsewhere — even from a remote peer —
// triggers the subscriber automatically.
graph.updateProperty(myRepo, "stars", 99);
Collaborative property types
import { ZodYText, ZodYArray } from "@codemix/y-graph-storage";
import { z } from "zod";
// Declare Y.Text / Y.Array / Y.Map properties in the schema
const schema = {
vertices: {
Document: {
properties: {
title: { type: ZodYText }, // collaborative string
tags: { type: ZodYArray(z.string()) }, // collaborative array
},
},
},
edges: {},
} as const satisfies GraphSchema;
// Plain values are auto-converted — no need to construct Y.* manually
const doc = graph.addVertex("Document", { title: "Hello", tags: ["crdt"] });
// Mutate in place — all peers see the change with no conflicts
doc.get("title").insert(5, ", world");
doc.get("tags").push(["graph"]);
Cypher queries for APIs and LLMs
The same graph is queryable via a Cypher-compatible string language — ideal for exposing data to LLMs via an MCP server, or accepting ad-hoc queries from external clients without bundling a traversal library.
Parse and execute
import { parseQueryToSteps, createTraverser } from "@codemix/graph";
const { steps, postprocess } = parseQueryToSteps(`
MATCH (u:User)-[:OWNS]->(r:Repo)
WHERE r.stars > 100
RETURN u.name, r.name
ORDER BY r.stars DESC
LIMIT 10
`);
const traverser = createTraverser(steps);
for (const row of traverser.traverse(graph, [])) {
console.log(postprocess(row));
// { u: { name: "Alice" }, r: { name: "my-repo" } }
}
Parameterised queries
// Pass parameters to avoid string interpolation
const { steps, postprocess } = parseQueryToSteps(`
MATCH (u:User { email: $email })-[:OWNS]->(r:Repo)
RETURN r.name, r.stars
`);
const traverser = createTraverser(steps);
const rows = Array.from(
traverser.traverse(graph, [{ email: "[email protected]" }])
).map(postprocess);
Mutations
// CREATE, MERGE, SET, DELETE are all supported
const { steps } = parseQueryToSteps(`
MATCH (r:Repo { name: $name })
SET r.stars = r.stars + 1
`);
createTraverser(steps).traverse(graph, [{ name: "my-repo" }]);
// Enforce read-only — throws ReadonlyGraphError on any write clause
const { steps: safeSteps } = parseQueryToSteps(query, { readonly: true });
License & History
This package is licensed under the MIT license.
It was orignally written as a research project byCharles Pick, founder ofcodemixand author of the infamousts-sqldemo. Later, when we were buildingcodemixwe needed a structured knowledge graph, so we adapted the code, addedY.jssupport and later, Opus 4.5 added a Cypher-like query language.
While you’re here
A single source of truth for your product. For humans and AI.
codemix captures what you actuallymean— your business domain, your user flows, the concepts, the constraints — and keeps it in sync with your codebase automatically.
Change your product through chat, diagrams, or collaborative editing. Steer coding agents through development and review code with real understanding. Every agent on your team shares the same context.
Create something completely new, or import your existing codebase to get started.
Build something brand new
Try codemix for free, no credit card required.
Similar Articles
colbymchenry/codegraph
CodeGraph is an open-source tool that creates a pre-indexed knowledge graph of a codebase, enabling Claude Code's exploration agents to query symbol relationships and call graphs instantly, reducing tool calls by up to 96% and exploration time by 77%.
Built my own agent runtime after hitting the ceiling with LangGraph — UI as graph nodes, Postgres durability, zero orchestration cost
The author introduces cascaide, a fullstack agent runtime and AI orchestration framework in TypeScript that runs anywhere JS/TS can. It offers UI as graph nodes, durable Postgres checkpointing, zero orchestration cost, and is designed to be self-hosted without vendor lock-in.
@yoheinakajima: i'm excited to open source Active Graph: an event-sourced reactive graph runtime for long-running, agents events/logs p…
Yohei Nakajima open-sources Active Graph, an event-sourced reactive graph runtime for long-running agents that provides persistent state, fork-and-diff agent runs, and full auditability, representing a new paradigm in agent architecture.
Show HN: HelixDB – A graph database built on object storage
HelixDB is a graph-vector database built in Rust for knowledge graphs and AI memory, offering a unified platform that supports graph, vector, KV, document, and relational data models, with tools for easy local and cloud deployment.
@tom_doerr: Modular GraphRAG implementation in Rust with WebGPU acceleration support. https://github.com/automataIA/graphrag-rs…
A modular, high-performance Rust implementation of GraphRAG (Graph-based Retrieval Augmented Generation) with support for WebGPU acceleration and three deployment architectures: server-only, WASM-only (client-side), and hybrid.