.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
| Option | Type | Default | Description |
|---|---|---|---|
Username | string? | null | Authentication username (null = skip auth) |
Password | string? | null | Authentication password |
AuthToken | BoltAuthToken? | null | Pre-constructed auth token (overrides Username/Password) |
Database | string? | null | Target database name |
UseTls | bool | false | Enable TLS (bolt+s://) |
AllowUntrustedCertificate | bool | false | Accept self-signed certificates |
ConnectionTimeout | TimeSpan? | 30 seconds | TCP connection timeout |
FetchSize | long | -1 | PULL batch size (-1 = fetch all, positive = batched streaming) |
Notifications | NotificationsConfig? | null | Notification severity and category filtering |
TelemetryDisabled | bool | false | Disable telemetry messages |
RoutingContext | Dictionary? | null | Cluster routing context |
Result Types
| Type | Description |
|---|---|
BoltResult | Complete result set: columns, records, summary |
BoltRecord | Single row with typed property access via indexer |
BoltNode | Node with labels and properties |
BoltEdge | Relationship with type and properties |
BoltPath | Graph path with alternating nodes and edges |
BoltPoint | Spatial point value |
BoltResultCursor | Lazy streaming cursor for batched PULL |
BoltTransaction | Transaction 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 stringIsTransient— whether the operation may succeed on retryGqlStatus/GqlStatusDescription— GQL status codesDiagnosticRecord— additional server diagnosticsCause— 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/awaitandValueTask - Connection pooling — via
BoltPoolwith lifetime tracking