A tiny, no-external-dependencies, disk-based graph database for Node.js with rich set of operations.
- Persist simple node-&-edge graphs in a JSON file, and query, traverse or mutate them entirely in JavaScript.
- TinyGraphDB is a great fit for building lightweight, GraphRAG systems — where LLMs retrieve knowledge via structured traversals instead of just flat vector search.
- ✅ Persistent storage
Automatically saves all nodes & relations to a JSON file on each change. - 🔍 Searchable
Find nodes or edges by name, metadata, IDs or via complex metadata filters. - 🔄 Traversals
Breadth-first-style traversals from any node or relation, with depth, direction and name filters. - 🔧 CRUD + Batch Ops
Create, read, update or delete single or multiple nodes/relations by search criteria. - 📈 Statistics
Get nodeCount, relationCount and avgDegree. - 🔄 Import/Export
Dump your graph into JSON, or load an existing JSON graph.
npm install tiny-graph-db
const TinyGraphDB = require('tiny-graph-db');
const db = new TinyGraphDB();
// Adding some sample data
const node1 = db.addNode('Document1', { type: 'document', content: 'AI research paper' });
const node2 = db.addNode('Concept1', { type: 'concept', domain: 'AI' });
const node3 = db.addNode('Author1', { type: 'person', name: 'John Doe' });
const rel1 = db.addRelation('contains', node1.id, node2.id, { confidence: 0.9 });
const rel2 = db.addRelation('authored_by', node1.id, node3.id, { confidence: 1.0 });
// Search examples
console.log('Search: nodes with type "concept":', db.searchNodes({ metadata: { type: 'concept' } }));
console.log('Search: relations with confidence > 0.8:', db.searchRelations({ metadata: { confidence: { gt: 0.8 } } }));
// Traversal examples
console.log('Traverse: from node1 (depth 2):', db.traverseFromNode(node1.id, { maxDepth: 2, directions: ['outgoing'] }));
console.log('Traverse: from metadata {type: "document"}:', db.traverseFromMetadata({ type: 'document' }, 1));
// Update example
db.updateNode(node1.id, { name: 'Updated Document', metadata: { updated: true } });
console.log('Graph stats:', db.getStats());
new TinyGraphDB(filePath?: string)
- filePath: path to JSON file for persistence (default: ./graph_data.json).
| addNode(name: string, metadata = {}) | Creates a node with given name & metadata | Node object |
| getNode(nodeId: string) | Fetches a node by its ID | Node or undefined |
| getAllNodes() | Returns all nodes | Node[] |
| updateNode(nodeId: string, updates) | Update name and/or metadata on a node | Updated Node |
| deleteNode(nodeId: string) | Deletes a node and all its connected relations | Deleted Node |
| deleteBySearch('node', conditions) | Deletes all nodes matching conditions | Node[] |
| addRelation(name, fromNodeId, toNodeId, metadata = {}) | Creates an edge between two existing nodes | Relation |
| getRelation(relationId: string) | Fetches a relation by ID | Relation or undefined |
| getAllRelations() | Returns all relations | Relation[] |
| updateRelation(relationId, updates) | Update name and/or metadata on a relation | Updated Relation |
| deleteRelation(relationId: string) | Deletes a single relation | Deleted Relation |
| deleteBySearch('relation', conditions) | Deletes all relations matching conditions | Relation[] |
traverseFromNode(startNodeId, options?)
traverseFromRelation(startRelationId, maxDepth?)
traverseFromMetadata(metadataConditions, maxDepth?)
-
options for traverseFromNode:
- maxDepth: number (default: ∞)
- directions: ['outgoing','incoming']
- relationName?: filter by relation name
-
Returns: an array of [ fromNode, relation, toNode ] tuples.
exportData(): { nodes: Node[]; relations: Relation[] }
importData(data: { nodes; relations })
- exportData() returns a JSON-serializable object.
- importData(...) wipes current graph and loads provided data, then persists.
searchNodes(conditions: SearchConditions): Node[]
searchRelations(conditions: SearchConditions): Relation[]
-
conditions:
-
name: string | RegExp | { contains: string }
-
id, fromNodeId, toNodeId
-
metadata: { [key]: valueOrFilter }
- supports { eq, ne, gt, gte, lt, lte, contains, startsWith, endsWith, in }
-
const TinyGraphDB = require('tiny-graph-db');
const db = new TinyGraphDB('./example_graph.json');
// ——— Create sample nodes ———
const book1 = db.addNode('Dune', { genre: 'sci-fi', pages: 412, published: 1965 });
const book2 = db.addNode('Foundation', { genre: 'sci-fi', pages: 255, published: 1951 });
const book3 = db.addNode('Hamlet', { genre: 'drama', pages: 160, published: 1603 });
const author1 = db.addNode('Frank Herbert', { nationality: 'US', awards: 2 });
const author2 = db.addNode('Isaac Asimov', { nationality: 'US', awards: 5 });
const author3 = db.addNode('William Shakespeare', { nationality: 'UK', awards: 0 });
// ——— Create sample relations ———
const rel1 = db.addRelation('wrote', book1.id, author1.id, { role: 'author' });
const rel2 = db.addRelation('wrote', book2.id, author2.id, { role: 'author' });
const rel3 = db.addRelation('wrote', book3.id, author3.id, { role: 'playwright' });
const rel4 = db.addRelation('influenced', author2.id, author1.id, { year: 1960 });
// Find the node whose name is exactly "Dune":
const result = db.searchNodes({ name: 'Dune' });
console.log(result);
// → [ { id: '…', name: 'Dune', metadata: { genre: 'sci-fi', … } } ]
// Find all nodes whose name contains "isaac" (will match "Isaac Asimov"):
const result = db.searchNodes({ name: { contains: 'isaac' } });
console.log(result.map(n => n.name));
// → [ 'Isaac Asimov' ]
// Find any book title that starts with 'F' or 'H':
const result = db.searchNodes({ name: /^F|^H/ });
console.log(result.map(n => n.name));
// → [ 'Foundation', 'Hamlet' ]
// All sci-fi books:
const scifi = db.searchNodes({ metadata: { genre: 'sci-fi' } });
// Books published before 1900:
const classics = db.searchNodes({
metadata: { published: { lt: 1900 } }
});
console.log(scifi.map(n => n.name)); // → [ 'Dune', 'Foundation' ]
console.log(classics.map(n => n.name)); // → [ 'Foundation', 'Hamlet' ]
// Find all authors from US or UK:
const authors = db.searchNodes({
metadata: { nationality: { in: ['US', 'UK'] } }
});
console.log(authors.map(a => a.name));
// → [ 'Frank Herbert', 'Isaac Asimov', 'William Shakespeare' ]
// All "wrote" relations:
const wroteRels = db.searchRelations({ name: 'wrote' });
// Relations where influence happened after 1950:
const influenceRels = db.searchRelations({
name: 'influenced',
metadata: { year: { gt: 1950 } }
});
console.log(wroteRels.length); // → 3
console.log(influenceRels); // → [ { id: '…', name: 'influenced', … } ]
// Sci-fi books by authors with >2 awards:
const sciFiBooks = db.searchNodes({
metadata: { genre: 'sci-fi' }
});
const topAuthors = db.searchNodes({
metadata: { awards: { gt: 2 } }
});
// Then filter relations:
const result = db
.getAllRelations()
.filter(r =>
r.name === 'wrote'
&& sciFiBooks.some(b => b.id === r.fromNodeId)
&& topAuthors.some(a => a.id === r.toNodeId)
);
console.log(result);
- getNeighbors(nodeId) List all adjacent nodes & relation directions.
- getStats() { nodeCount, relationCount, avgDegree }
- Fork the repo
- Create a feature branch git checkout -b feat/my-feature
- Commit your changes (git commit -m 'Add awesome feature')
- Push (git push origin feat/my-feature)
- Open a Pull Request
Please file issues for bugs or feature requests via GitHub Issues.
This project is licensed under the MIT License. See LICENSE for details.
Built with ♥ by freakynit
.png)
![Rockstar fired developers looks like "union busting" [video]](https://www.youtube.com/img/desktop/supported_browsers/edgium.png)