.NET Bolt Client

High-performance Bolt protocol client for .NET with zero external dependencies, pipelining, and connection pooling.

dotnet add package Traverse.Bolt

Targets net10.0. Works with Traverse, Neo4j, and any Bolt 5.1+ compatible server.

Connect

using Traverse.Bolt;

// Simple connection
var client = await BoltClient.ConnectAsync("localhost", 7690);

// With options
var client = await BoltClient.ConnectAsync("localhost", 7690,
    new BoltClientOptions
    {
        Username = "admin",
        Password = "s3cret",
        Database = "mydb",
        UseTls = false,
        ConnectionTimeout = TimeSpan.FromSeconds(30)
    });

// From URI
var client = await BoltClient.ConnectAsync(
    "bolt+s://localhost:7690",
    new BoltClientOptions { AllowUntrustedCertificate = true });

Supported URI schemes: bolt://, bolt+s:// (TLS), bolt+ssc:// (TLS + self-signed).

Run Queries

// Execute a query and get full results
var result = await client.RunAsync(
    "MATCH (n:Person) WHERE n.age > $minAge RETURN n.name, n.age",
    new Dictionary<string, object> { ["minAge"] = 25 });

foreach (var record in result.Records)
{
    var name = record["n.name"];
    var age = record["n.age"];
}

Consume Without Materializing

// Drain the result stream for write operations (zero-copy, zero-parse)
var summary = await client.RunAndConsumeAsync(
    "CREATE (n:Person {name: 'Alice'}) RETURN n");

// Discard results server-side (DISCARD instead of PULL)
var summary = await client.RunAndDiscardAsync(
    "CREATE (n:Temp)");

Streaming Cursor

// Lazy streaming with batched PULL
var cursor = await client.RunStreamAsync("MATCH (n) RETURN n");
while (await cursor.FetchAsync())
{
    var record = cursor.Current;
    Console.WriteLine(record);
}

Transactions

// BeginTransactionAsync returns a BoltTransaction
var tx = await client.BeginTransactionAsync();

await client.RunAsync("CREATE (n:Person {name: 'Bob'})");
await client.RunAsync("CREATE (n:Person {name: 'Carol'})");

await tx.CommitAsync();
// or: await tx.RollbackAsync();

Re-Authentication

// Switch users on the same connection (Bolt 5.4+)
await client.LogoffAsync();
await client.LogonAsync("other_user", "other_pass");

Connection Pooling

// Create a connection pool
var pool = new BoltPool("localhost", 7690,
    new BoltPoolOptions { Database = "mydb" });

// Acquire a pooled client
using var pooled = await pool.AcquireAsync();
var result = await pooled.RunAsync("MATCH (n) RETURN count(n)");

Routing

// Discover cluster routing table
var routingTable = await client.RouteAsync();

Connection Options

OptionTypeDefaultDescription
Usernamestring?nullAuthentication username (null = skip auth)
Passwordstring?nullAuthentication password
AuthTokenBoltAuthToken?nullPre-constructed auth token (overrides Username/Password)
Databasestring?nullTarget database name
UseTlsboolfalseEnable TLS (bolt+s://)
AllowUntrustedCertificateboolfalseAccept self-signed certificates
ConnectionTimeoutTimeSpan?30 secondsTCP connection timeout
FetchSizelong-1PULL batch size (-1 = fetch all, positive = batched streaming)
NotificationsNotificationsConfig?nullNotification severity and category filtering
TelemetryDisabledboolfalseDisable telemetry messages
RoutingContextDictionary?nullCluster routing context

Result Types

TypeDescription
BoltResultComplete result set: columns, records, summary
BoltRecordSingle row with typed property access via indexer
BoltNodeNode with labels and properties
BoltEdgeRelationship with type and properties
BoltPathGraph path with alternating nodes and edges
BoltPointSpatial point value
BoltResultCursorLazy streaming cursor for batched PULL
BoltTransactionTransaction handle with CommitAsync / RollbackAsync

Error Handling

try
{
    await client.RunAsync("INVALID CYPHER");
}
catch (BoltException ex)
{
    Console.WriteLine(ex.Code);        // "Neo.ClientError.Statement.SyntaxError"
    Console.WriteLine(ex.IsTransient); // false — safe to not retry
}

BoltException includes:

  • Code — error classification string
  • IsTransient — whether the operation may succeed on retry
  • GqlStatus / GqlStatusDescription — GQL status codes
  • DiagnosticRecord — additional server diagnostics
  • Cause — chained nested cause

Key Design Choices

  • Zero external dependencies — uses only .NET framework APIs
  • Pipelined RUN+PULL — sends RUN and PULL in a single TCP write for lower round-trip latency
  • Bolt 5.1–6.0 — supports HELLO/LOGON handshake, re-authentication, telemetry
  • Fully async — all I/O uses async/await and ValueTask
  • Connection pooling — via BoltPool with lifetime tracking