Skip to content

GrafeoDB/grafeo-web

Repository files navigation

@grafeo-db/web

CI codecov npm License

Grafeo graph database in the browser.

Zero backend. Data stays on the client.

Features

  • Zero backend: Grafeo runs entirely in the browser via WebAssembly
  • Persistent storage: IndexedDB keeps data across sessions
  • Non-blocking: Web Worker execution keeps the UI responsive
  • Multi-language queries: GQL, Cypher, SPARQL, SQL, Gremlin, GraphQL
  • Parameterized queries: bind $name-style parameters safely
  • Vector search: HNSW indexes with k-NN and MMR search
  • Full-text search: BM25 text indexes with hybrid (text + vector) search
  • Bulk import: LPG nodes/edges, RDF triples, or tabular rows
  • Framework integrations: React, Vue, Svelte
  • TypeScript-first: Complete type definitions

Installation

npm install @grafeo-db/web

Quick Start

import { GrafeoDB } from '@grafeo-db/web';

// In-memory database
const db = await GrafeoDB.create();

// Or persist to IndexedDB
const db = await GrafeoDB.create({ persist: 'my-database' });

// Create data
await db.execute(`INSERT (:Person {name: 'Alice', age: 30})`);
await db.execute(`INSERT (:Person {name: 'Bob', age: 25})`);
await db.execute(`
  MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
  INSERT (a)-[:KNOWS {since: 2020}]->(b)
`);

// Query
const result = await db.execute(`
  MATCH (p:Person)-[:KNOWS]->(friend)
  RETURN p.name, friend.name
`);

for (const row of result) {
  console.log(`${row['p.name']} knows ${row['friend.name']}`);
}

// Check version
console.log(GrafeoDB.version()); // e.g. "0.5.27"

// Cleanup
await db.close();

Multi-Language Queries

// GQL (default)
await db.execute(`MATCH (p:Person) RETURN p.name`);

// Cypher
await db.execute(`MATCH (p:Person) RETURN p.name`, { language: 'cypher' });

// SPARQL
await db.execute(`SELECT ?name WHERE { ?p a :Person ; :name ?name }`, { language: 'sparql' });

// SQL
await db.execute(`SELECT name FROM Person`, { language: 'sql' });

Supported: gql, cypher, sparql, sql, gremlin, graphql.

Parameterized Queries

const result = await db.execute(
  `MATCH (p:Person {name: $name}) RETURN p.age`,
  { params: { name: 'Alice' } },
);

Parameters work with all query languages and in the lite build.

API

GrafeoDB

Core

Method Description
GrafeoDB.create(options?) Create a database instance
GrafeoDB.version() Get the WASM engine version
db.execute(query, options?) Execute a query, returns Record<string, unknown>[]
db.executeRaw(query, options?) Execute a query, returns columns + rows + timing
db.nodeCount() Number of nodes
db.edgeCount() Number of edges
db.schema() Schema info: labels, edge types, property keys
db.memoryUsage() Hierarchical WASM heap usage breakdown
db.setSchema(name) Set current schema context
db.resetSchema() Clear current schema context
db.currentSchema() Get current schema name (or undefined)
db.clearPlanCache() Clear cached query plans
db.isOpen Whether the database is still open
db.close() Release WASM memory and cleanup

Persistence & Snapshots

Method Description
db.export() Export full database as a snapshot
db.import(snapshot) Restore from a snapshot
db.clear() Delete all data
db.storageStats() IndexedDB usage and quota

Import

Method Description
db.importRows(rows, options) Import an array of objects as nodes or edges
db.importLpg(data) Import LPG nodes and edges in one call
db.importRdf(data) Import RDF triples (requires rdf WASM feature)

Vector Search (requires vector-index WASM feature)

Method Description
db.createVectorIndex(label, property, options?) Create an HNSW vector index
db.dropVectorIndex(label, property) Drop a vector index
db.rebuildVectorIndex(label, property) Drop and recreate, preserving config
db.vectorSearch(label, property, query, k, options?) k-NN search returning [{id, distance}]
db.mmrSearch(label, property, query, k, options?) MMR search for diverse results

Text & Hybrid Search (requires text-index / hybrid-search WASM features)

Method Description
db.createTextIndex(label, property) Create a BM25 text index
db.dropTextIndex(label, property) Drop a text index
db.rebuildTextIndex(label, property) Rebuild a text index
db.textSearch(label, property, query, k) Full-text search returning [{id, score}]
db.hybridSearch(label, textProp, vectorProp, queryText, k) Combined BM25 + vector search

CreateOptions

{
  persist?: string;          // IndexedDB key for persistence
  worker?: boolean;          // Run WASM in a Web Worker
  persistInterval?: number;  // Debounce interval in ms (default: 1000)
}

ExecuteOptions

{
  language?: 'gql' | 'cypher' | 'sparql' | 'sql' | 'gremlin' | 'graphql';
  params?: Record<string, unknown>;  // $name-style parameters
}

Persistence

Data persists to IndexedDB automatically:

// First visit - creates database
const db = await GrafeoDB.create({ persist: 'my-app' });
await db.execute(`INSERT (:User {name: 'Alice'})`);

// Later visit - data is still there
const db = await GrafeoDB.create({ persist: 'my-app' });
const result = await db.execute(`MATCH (u:User) RETURN u.name`);
// -> [{ 'u.name': 'Alice' }]

Persistence only triggers on mutating queries (INSERT, CREATE, DELETE, etc.), not on reads.

Storage Management

// Check storage usage
const stats = await db.storageStats();
console.log(`Using ${stats.bytesUsed} of ${stats.quota} bytes`);

// Export database
const snapshot = await db.export();

// Import into another database
const db2 = await GrafeoDB.create();
await db2.import(snapshot);

// Clear all data
await db.clear();

Web Worker Mode

For large databases or complex queries, run in a Web Worker:

const db = await GrafeoDB.create({
  worker: true,
  persist: 'large-database',
});

// Queries run in background thread - UI stays responsive
const result = await db.execute(`MATCH (a)-[*1..5]->(b) RETURN count(*)`);

Bulk Import

LPG (nodes + edges)

await db.importLpg({
  nodes: [
    { labels: ['Person'], properties: { name: 'Alice' } },
    { labels: ['Person'], properties: { name: 'Bob' } },
  ],
  edges: [
    { source: 0, target: 1, type: 'KNOWS', properties: { since: 2020 } },
  ],
});

Edge source/target are zero-based indexes into the nodes array from the same batch.

Tabular rows

// Import as nodes
await db.importRows(
  [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }],
  { mode: 'nodes', label: 'Person' },
);

// Import as edges
await db.importRows(
  [{ source: 1, target: 2, weight: 0.5 }],
  { mode: 'edges', edgeType: 'CONNECTS' },
);

RDF triples (requires rdf WASM feature)

await db.importRdf({
  triples: [
    { subject: 'http://ex.org/alice', predicate: 'http://ex.org/name', object: { value: 'Alice' } },
    { subject: 'http://ex.org/alice', predicate: 'http://ex.org/knows', object: 'http://ex.org/bob' },
  ],
});

Framework Integrations

React

import { useGrafeo, useQuery } from '@grafeo-db/web/react';

function App() {
  const { db, loading, error } = useGrafeo({ persist: 'my-app' });

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return <PersonList db={db} />;
}

function PersonList({ db }) {
  const { data, loading, refetch } = useQuery(
    db,
    `MATCH (p:Person) RETURN p.name`,
  );

  if (loading) return <div>Loading...</div>;

  return (
    <ul>
      {data.map((row, i) => (
        <li key={i}>{row['p.name']}</li>
      ))}
    </ul>
  );
}

Vue

<script setup>
import { useGrafeo, useQuery } from '@grafeo-db/web/vue';

const { db, loading, error } = useGrafeo({ persist: 'my-app' });
const { data } = useQuery(db, `MATCH (p:Person) RETURN p.name`);
</script>

Svelte

<script>
  import { createGrafeo } from '@grafeo-db/web/svelte';

  const { db, loading, error } = createGrafeo({ persist: 'my-app' });
</script>

{#if $loading}Loading...{/if}
{#if $error}Error: {$error.message}{/if}

Lite Build

A smaller build with GQL support only (no multi-language parsers or AI search features):

import { GrafeoDB } from '@grafeo-db/web/lite';

const db = await GrafeoDB.create();
await db.execute(`MATCH (n) RETURN n`);

// Parameterized queries work in lite too
await db.execute(`MATCH (p {name: $name}) RETURN p`, { params: { name: 'Alice' } });

Browser Support

Browser Version
Chrome 89+
Firefox 89+
Safari 15+
Edge 89+

Requires WebAssembly, IndexedDB and Web Workers.

Limitations

Constraint Limit
Database size ~500 MB (IndexedDB quota)
Memory ~256 MB (WASM heap)
Concurrency Single writer, multiple readers
changesSince() Returns [] (pending WASM change tracking)

For larger datasets, use Grafeo server-side.

Development

npm run build      # Build all entries via tsup
npm test           # Run tests (vitest, 145 tests)
npm run typecheck  # Type check (tsc --noEmit)

Related

Package Use Case
grafeo Rust crate
@grafeo-db/wasm Raw WASM binary

License

Apache-2.0

About

Grafeo graph database in the browser. Zero backend. Your data stays on the client.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors