Skip to content

stefanak-michal/bolt.js

Repository files navigation

Bolt protocol js driver

JavaScript/TypeScript library for communication with graph databases over WebSocket using the Bolt protocol specification. Bolt protocol was created by Neo4j and documentation is available at https://www.neo4j.com/docs/bolt/current/. This library is aimed to be low level, support all available versions and keep up with protocol messages architecture and specifications.

ko-fi

NPM Version GitHub commits since latest release NPM Downloads

⚠️ This project was made as alternative for Neo4j official library which by how it's structured is vendor-locked without support for older bolt versions.

🏷️ Version support

This library supports Bolt <= 6.

πŸ“š Supported ecosystems

This library doesn't guarantee that each ecosystem has Bolt protocol implemented correctly.

βœ… Requirements

  • Node.js 18+
  • TypeScript 5+ (if using TypeScript)

πŸ’Ύ Installation

Within your node.js project use following command:

npm install @stefanak-michal/bolt-protocol

Or in your website include following code:

<script src="https://cdn.jsdelivr.net/npm/@stefanak-michal/bolt-protocol@1"></script>

πŸ–₯️ Usage

Concept of usage is based on Bolt messages. Bolt messages are mapped 1:1 as protocol methods. Available protocol methods depend on the Bolt version. Communication works in pipeline and you can chain multiple Bolt messages before consuming responses from the server.

The main Bolt class handles the initial handshake and returns the appropriate protocol version instance. Basic usage consists of query execution and fetching responses split in two methods. First message run is for sending queries. Second message pull is for fetching the response from the executed query. The response for pull always contains n+1 rows because the last entry is a success message with meta information.

ℹ️ More info about available Bolt messages: https://www.neo4j.com/docs/bolt/current/bolt/message/

Available methods

Bolt class

Method Description Return
Bolt.connect Static factory. Creates a connection, executes the handshake and returns a protocol version instance. AProtocol

Protocol class

Method Description
hello Initialize connection with the server
logon Authenticate the user (Bolt 5.1+)
logoff Log out the authenticated user (Bolt 5.1+)
run Execute a query. Response contains meta information.
pull Pull results from an executed query
discard Discard results waiting for pull
begin Start a transaction
commit Commit the current transaction
rollback Rollback the current transaction
reset Reset the connection state
route Fetch cluster routing information
telemetry Send telemetry data (Bolt 5.4+)
getResponse Await and return the next pending server response
getResponses Async generator yielding all pending server responses
init @see hello (Bolt 1/2)
pullAll @see pull (Bolt 1/2)
discardAll @see discard (Bolt 1/2)

Multiple methods accept an extra argument. This argument can contain any key-value pairs defined by the Bolt specification. The content of extra changed across Neo4j versions. Check the Bolt documentation for the specific version you are working with.

Authentication

logon (Bolt 5.1+) and hello (Bolt < 5.1) both accept an auth object. The object must contain a scheme key and optionally principal and credentials depending on the chosen scheme.

scheme principal credentials
none
basic username password
bearer token
kerberos token

Transactions

Bolt from version 3 supports explicit transactions via the following methods:

  • begin
  • commit
  • rollback

run executes a query in an auto-commit transaction if no explicit transaction is open.

Example

import { Bolt } from 'bolt.js';

// Connect and negotiate the protocol version
// Defaults to WebSocketChannel and uri bolt://127.0.0.1:7687
const protocol = await Bolt.connect();

// Initialize connection with the server
let response = await protocol.hello({ user_agent: 'my-app/1.0' });
// verify response for successful initialization

// Authenticate (Bolt 5.1+)
response = await protocol.logon({ scheme: 'basic', principal: 'neo4j', credentials: 'neo4j' });
// verify response for successful login

// Pipeline two messages: execute a query with parameters, then pull records
protocol.run('RETURN $a AS num, $b AS str', { a: 123, b: 'text' });
protocol.pull();

// Consume all pending server responses
for await (const resp of protocol.getResponses()) {
    // resp is an instance of Response
    // First response:  SUCCESS for RUN
    // Second response: RECORD for PULL (one per row)
    // Third response:  SUCCESS for PULL
    if (resp.isRecord) {
        console.log(resp.content);
    }
}

πŸ“¦ Bundle usage

The library is also available as a pre-built UMD bundle:

<script src="dist/bolt.js"></script>
<script>
    const { Bolt, WebSocketChannel } = BoltDriver;
</script>

πŸ‘₯ Client helper class

The library includes a high-level Client helper class for simplified interaction with a graph database. It wraps common operations (authentication, queries, and transactions) so you don't have to manage protocol messages and responses manually.

Constructor

new Client(protocol: AProtocol)

Methods

Method Description Return
login(auth: AuthToken) Authenticate. Automatically adapts to the protocol version. Promise<Response | Response[] | void>
logout() Log out (Bolt 5.1+). Promise<Response>
query(cypher, parameters?, extra?) Execute a query and return all rows as arrays of values. Promise<Record<string, unknown>[]>
beginTransaction(extra?) Begin a transaction. Promise<Response>
commit() Commit the current transaction. Promise<Response>
rollback() Rollback the current transaction. Promise<Response>

Example

import { Bolt, Client } from 'bolt.js';

const protocol = await Bolt.connect();
const client = new Client(protocol);
await client.login({ scheme: 'basic', principal: 'neo4j', credentials: 'neo4j' });

// Returns all rows as arrays keyed by field name
const rows = await client.query('MATCH (n:Person) RETURN n.name AS name, n.age AS age');
// rows = [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]

// Transaction example
await client.beginTransaction();
await client.query('CREATE (n:Person {name: $name})', { name: 'Charlie' });
await client.commit(); // or client.rollback()

🚦 Server state

Server state is not reported by the server but is derived from received responses. You can access the current state through protocol.serverState. The property is updated with every getResponse / getResponses call.

πŸ“Š Cypher type mapping

Neo4j JavaScript
Null null
Boolean boolean
Integer number or BigInt
Float number
String string
List Array
Dictionary plain object
Node Node structure class
Relationship Relationship structure class
UnboundRelationship UnboundRelationship structure class
Path Path structure class
Date / Time etc. Temporal structure classes (Date, Time, …)
Point Point2D / Point3D structure classes
Vector (Bolt 6) Vector structure class

Integer vs Float

JavaScript has a single number type, so the library infers the Bolt type automatically: a number with no decimal part is sent as Integer, a number with a decimal part is sent as Float.

When you need to override this, for example, sending 5 as a Float or truncating 3.9 to an Integer, wrap the value in the provided helper classes:

import { Integer, Float } from '@stefanak-michal/bolt-protocol';

// Sent as Bolt Float even though the value has no decimal part
protocol.run('CREATE (n:Node {x: $x})', { x: new Float(5) });

// Sent as Bolt Integer, decimal part is truncated
protocol.run('CREATE (n:Node {count: $count})', { count: new Integer(3.9) });

These wrappers work anywhere a value is accepted: query parameters, list elements, dictionary values, and nested structures.

About

Low-level JavaScript/TypeScript Bolt protocol driver for Neo4j and other Bolt-compatible graph databases

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

  •  

Contributors