Node.js Embedded

Run Traverse as an in-process graph database in Node.js applications.

Install

npm install @truespar/traverse-node

Note: The package includes prebuilt native bindings via napi-rs. Supported on Windows, Linux, and macOS.

Open a Database

const traverse = require('@truespar/traverse-node');

// Open or create a database (file is created on first flush)
const db = traverse.open('mydb.tvdb');

Run Queries

const tx = db.begin();
const result = tx.execute('MATCH (n:Person) RETURN n.name, n.age');
for (const row of result.rows) {
    console.log(row['n.name'], row['n.age']);
}
tx.abort();  // read-only, no changes to commit

Query Parameters

const tx = db.begin();
const result = tx.execute(
    'MATCH (n:Person) WHERE n.age > $minAge RETURN n.name',
    { minAge: 25 }
);
for (const row of result.rows) {
    console.log(row['n.name']);
}
tx.abort();

Write Data

const tx = db.begin();
tx.execute("CREATE (p:Person {name: 'Alice', age: 30})");
tx.execute("CREATE (p:Person {name: 'Bob', age: 25})");
tx.commit();

Note: You must call commit() to persist changes. If neither commit() nor abort() is called, changes are discarded when the transaction is garbage collected.

Transactions

let tx = db.begin();
tx.execute("CREATE (p:Person {name: 'Carol'})");
tx.execute(
    "MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Carol'}) " +
    "CREATE (a)-[:KNOWS]->(b)"
);
tx.commit();

// Verify
tx = db.begin();
const result = tx.execute('MATCH (a)-[:KNOWS]->(b) RETURN a.name, b.name');
console.log(result.rows[0]['a.name'], result.rows[0]['b.name']);
tx.abort();

QueryResult

const tx = db.begin();
const result = tx.execute('MATCH (n:Person) RETURN n.name, n.age');

console.log(result.columns);   // ['n.name', 'n.age']
console.log(result.rowCount);  // number of rows

// Rows are an array of objects
for (const row of result.rows) {
    console.log(row['n.name']);
}

// Mutation statistics
const writeResult = tx.execute('CREATE (n:Test)');
console.log(writeResult.stats.nodesCreated);  // 1
tx.abort();

Bulk Writer

The BulkWriter bypasses MVCC for high-throughput imports (2–5M rows/s).

const writer = db.bulkWriter();
const nid1 = writer.createNode(['Person'], { name: 'Alice', age: 30 });
const nid2 = writer.createNode(['Person'], { name: 'Bob', age: 25 });
writer.createEdge(nid1, nid2, 'KNOWS', { since: 2024 });
writer.commit();

Nodes can be created with labels only:

const nid = writer.createNode(['Company', 'Active']);

Edges can be created without properties:

writer.createEdge(sourceId, targetId, 'WORKS_AT');

Indexes

Create indexes via the bulk writer or Cypher:

// Via BulkWriter
const writer = db.bulkWriter();
writer.createIndex('idx_person_name', 'Person', 'name');
writer.createEdgeIndex('idx_knows_since', 'KNOWS', 'since');
writer.createEdgeIndex('idx_knows', 'KNOWS');  // type-only index
writer.commit();

// Via Cypher
const tx = db.begin();
tx.execute('CREATE INDEX idx_person_age FOR (n:Person) ON (n.age)');
tx.commit();

Drop an edge index:

const writer = db.bulkWriter();
writer.dropEdgeIndex('idx_knows_since');
writer.commit();

Flush and Close

// Flush dirty pages to disk
db.flush();

// Close the database
db.close();

Note: Traverse is an in-memory database. Call flush() to persist data to the .tvdb file. Data written since the last flush is lost if the process crashes.