Skip to content

Transactions

Transactions group multiple operations into an atomic unit: either all succeed (commit) or all are rolled back.

Basic Usage

const tx = db.beginTransaction();
try {
  await tx.execute("INSERT (:Person {name: 'Alix'})");
  await tx.execute("INSERT (:Person {name: 'Gus'})");
  await tx.execute("MATCH (a:Person {name: 'Alix'}), (b:Person {name: 'Gus'}) INSERT (a)-[:KNOWS]->(b)");
  tx.commit();
} catch (err) {
  tx.rollback();
  throw err;
}

Using using (Node.js 24+)

With explicit resource management (Node.js 24+, or TypeScript compiled to an older target), the transaction rolls back at the end of its block if commit() is not called:

{
  using tx = db.beginTransaction();
  await tx.execute("INSERT (:Person {name: 'Harm'})");
  tx.commit();
  // If an exception occurs before commit, tx is automatically rolled back
}

Isolation Levels

// Default: snapshot isolation
const tx = db.beginTransaction();

// Explicit isolation level
const tx = db.beginTransaction('read_committed');

Available levels:

Level Description
read_committed See committed data from other transactions
snapshot (default) See a consistent snapshot from transaction start
serializable Not available yet: refused for now: snapshot isolation is the strongest level until queries record their reads

Query Languages in Transactions

All query languages work inside transactions:

const tx = db.beginTransaction();
await tx.execute("INSERT (:Person {name: 'Alix'})");           // GQL
await tx.executeCypher("CREATE (:Person {name: 'Gus'})");      // Cypher
await tx.executeSparql("PREFIX : <http://example.org/> INSERT DATA { :s :p :o }"); // SPARQL
await tx.executeGremlin("g.addV('Person').property('name','Harm')"); // Gremlin
tx.commit();

Checking Transaction State

const tx = db.beginTransaction();
console.log(tx.isActive); // true

tx.commit();
console.log(tx.isActive); // false

Error Handling

If a query fails inside a transaction, the transaction remains active. You can retry or rollback:

const tx = db.beginTransaction();
try {
  await tx.execute("INSERT (:Person {name: 'Alix'})");
  await tx.execute("INVALID SYNTAX"); // throws
} catch (err) {
  console.error('Query failed:', err.message);
  // Transaction is still active, you can retry or rollback
  tx.rollback();
}

Errors from the Grafeo engine are thrown as standard JavaScript Error objects. The message is prefixed with the error category:

  • "Query error: ..." for syntax or execution errors
  • "Transaction error: ..." for conflicts or state errors
  • "Database closed: ..." for a write after close()
  • "Database error: ..." for storage or configuration errors

An error from the database also carries errorCode (the Grafeo code, such as GRAFEO-T001 for a write conflict), gqlStatus (its ISO GQL status, such as 40000) and isRetryable, whether running the transaction again can succeed (see Error Codes):

try {
  tx.commit();
} catch (err) {
  if (err.isRetryable) {
    // run the whole transaction again
  }
}

A commit that fails ends the transaction: isActive is false afterwards, rollback() does nothing and another commit() throws. START TRANSACTION, COMMIT and ROLLBACK through tx.execute throw with GRAFEO-T004: end the transaction with commit() or rollback().