Skip to main content
graphwiz.ai
← Back to neo4j

Neo4j Database Adapters Compared: Official and Community Drivers Across Every Major Language

Neo4j's core protocol has settled on Bolt as the primary wire protocol since version 4.0, unifying how drivers communicate with the database regardless of language. But the developer experience varies significantly between drivers — from type handling and transaction models to async support and connection pooling. This article maps the landscape so you know what you're getting before you pip install, npm install, or go get.

Official Drivers: The Five Pillars

Neo4j maintains five drivers as first-class citizens, all aligned on the same versioning scheme, the same Bolt protocol support, and the same feature set. They share a common design philosophy: one driver instance per application, sessions for units of work, and transaction functions with automatic retry.

Python — neo4j (formerly neo4j-driver)

The Python driver is the most actively developed, now at v6.2.0 (May 2026). The package was renamed from neo4j-driver starting with 6.0.0 — the old name is deprecated and receives no further updates.

from neo4j import GraphDatabase

driver = GraphDatabase.driver(
    "neo4j+s://demo.neo4j.io",
    auth=("neo4j", "password")
)

with driver.session(database="neo4j") as session:
    result = session.run(
        "MATCH (n:Repository) RETURN n.name AS name LIMIT 10"
    )
    for record in result:
        print(record["name"])

Key characteristics:

  • Python ≥3.10 required, supports up to 3.14
  • Bolt 6.0, 5.x, and 4.4 protocol support
  • Optional Rust extensions (neo4j-rust-ext) for performance-critical paths — not installed by default, opt in with pip install neo4j[rust-ext]
  • Reactive streams via the experimental rx module
  • Server notification warnings automatically emitted since v5.21

The Rust extension layer is unique among the official drivers — it replaces the pure-Python Bolt serialisation with native Rust code, yielding measurable throughput gains for bulk operations.

JavaScript — neo4j-driver

The JS driver supports both Node.js (≥18) and browser environments (via WebSocket). It reached v5.28.3 in January 2026 with a v6.0.1 alpha on npm. The v6.x line introduces the Neo4j Vector type for embedding storage.

import neo4j from "neo4j-driver";

const driver = neo4j.driver(
  "neo4j+s://demo.neo4j.io",
  neo4j.auth.basic("neo4j", "password")
);

const session = driver.session({ database: "neo4j" });
try {
  const result = await session.run(
    "MATCH (n:Repository) RETURN n.name AS name LIMIT 10"
  );
  result.records.forEach((record) => console.log(record.get("name")));
} finally {
  await session.close();
}

Key characteristics:

  • ESM and CJS exports — import or require as needed
  • Browser bundle ships a WebSocket-based driver for client-side apps
  • Integer handling — Neo4j's 64-bit integers require explicit .toNumber() or neo4j.int() conversion; the driver does not auto-coerce to JavaScript Number (which loses precision above 2⁵³)
  • RxJS as a dependency for reactive query results
  • 6.x introduces the Vector type for embedding queries, with significant wire-protocol optimisation over sending float arrays as lists

Java — neo4j-java-driver

The Java driver is the reference implementation and the most mature. It targets Java 8+ and is available as a Maven dependency.

<dependency>
  <groupId>org.neo4j.driver</groupId>
  <artifactId>neo4j-java-driver</artifactId>
  <version>5.28.0</version>
</dependency>
var driver = GraphDatabase.driver(
    "neo4j+s://demo.neo4j.io",
    AuthTokens.basic("neo4j", "password")
);

try (var session = driver.session(SessionConfig.forDatabase("neo4j"))) {
    var result = session.run("MATCH (n:Repository) RETURN n.name LIMIT 10");
    while (result.hasNext()) {
        System.out.println(result.next().get("name").asString());
    }
}

Key characteristics:

  • Reactive Streams API — the driver core depends on the Reactive Streams spec, making it naturally compatible with Project Reactor and RxJava
  • CompletableFuture-based async API alongside blocking and reactive
  • Object mapping via @Node, @Relationship, and @Property annotations in the Spring Data Neo4j ecosystem
  • Bookmark management built into session semantics for causal consistency across clusters
  • Gradle / Maven dependency management

Go — neo4j-go-driver

The Go driver reached v6.0.0 in December 2025, requiring Go 1.24+. Its API follows Go idioms closely, using context.Context throughout.

import (
    "context"
    "github.com/neo4j/neo4j-go-driver/v5/neo4j"
)

func main() {
    ctx := context.Background()
    driver, _ := neo4j.NewDriverWithContext(
        "neo4j+s://demo.neo4j.io",
        neo4j.BasicAuth("neo4j", "password", ""),
    )
    defer driver.Close(ctx)

    session := driver.NewSession(ctx, neo4j.SessionConfig{})
    defer session.Close(ctx)

    result, _ := session.Run(ctx,
        "MATCH (n:Repository) RETURN n.name AS name LIMIT 10", nil)
    for result.Next(ctx) {
        fmt.Println(result.Record().Values[0])
    }
}

Key characteristics:

  • context.Context everywhere — cancellation, timeouts, and tracing flow through every API call
  • Goroutine-safe driver and session: a single driver instance serves all goroutines
  • No reflection-based mapping — records must be destructured manually via .Values, .Get, or type assertions
  • WithContext variants on every operation — no global implicit context

.NET — Neo4j.Driver

The .NET driver mirrors the Java driver's API and is available via NuGet. It supports both synchronous and asynchronous idioms.

using Neo4j.Driver;

var driver = GraphDatabase.Driver(
    "neo4j+s://demo.neo4j.io",
    AuthTokens.Basic("neo4j", "password")
);

await using var session = driver.AsyncSession();
var result = await session.RunAsync(
    "MATCH (n:Repository) RETURN n.name AS name LIMIT 10"
);
await foreach (var record in result)
{
    Console.WriteLine(record["name"].As<string>());
}

Key characteristics:

  • IAsyncEnumerable for streaming results (C# 8+)
  • Fluent transaction functions with automatic retry: session.ExecuteReadAsync(tx => ...)
  • Full Aura compatibility — the driver handles the Aura-specific TLS configuration transparently
  • .NET 6+ target framework

Community Drivers

Beyond the five official drivers, several community-maintained adapters fill gaps for languages that Neo4j doesn't officially support. Their maturity varies significantly.

LanguageDriverProtocolBolt VersionClusterTestkit
Rustneo4rsBolt only4.0–4.3
Rustneo4j-rust-driverBolt4.4–6.0
PHPneo4j-php-clientBolt + HTTP4.x–5.x
Rubyneo4j-ruby-driverBolt4.x–5.x✓ (FFI)
ElixirBoltxBolt4.x
PerlNeo4j::DriverBolt + HTTP4.x
Clibneo4j-omniBolt4.x

Rust — Two Contenders

The Rust ecosystem has two drivers with different trade-offs. neo4rs (v0.8, crates.io) is the older project, using tokio, but only supports Bolt 4.0–4.3, which means it cannot connect to Neo4j 5.x+ without protocol downgrade. The newer neo4j-rust-driver by robsdedude mirrors the official driver API more closely, supports Bolt 4.4–6.0 (including vector types and re-authentication), and passes the official TestKit. However, it remains a hobby project with a single maintainer.

use neo4rs::Graph;

let graph = Graph::connect(
    "bolt://neo4j:password@localhost:7687"
).await.unwrap();

let mut result = graph.execute(
    "MATCH (n:Repository) RETURN n.name AS name LIMIT 10"
).await.unwrap();

while let Ok(Some(row)) = result.next().await {
    let name: String = row.get("name").unwrap();
    println!("{}", name);
}

PHP — neo4j-php-client

The PHP client stands out among community drivers for its quality: it is testkit-validated (the same test suite the official drivers use), developed in collaboration with the Neo4j driver team, and supports Bolt, HTTP, and auto-routed drivers simultaneously in a single client. It is fully typed with Psalm and handles cluster routing, Aura, and OIDC authentication.

$client = ClientBuilder::create()
    ->withDriver('bolt', 'bolt+s://user:password@localhost')
    ->withDriver('neo4j', 'neo4j://neo4j.test.com', Authenticate::oidc('token'))
    ->withDefaultDriver('bolt')
    ->build();

$result = $client->run('MATCH (n:Repository) RETURN n.name LIMIT 10');
foreach ($result as $record) {
    echo $record->get('name') . "\n";
}

Ruby — neo4j-ruby-driver

The Ruby driver from the neo4jrb organisation ships two implementations in one gem: a pure Ruby driver (MRI ≥3.1) and a JRuby wrapper around the official Java driver. The API mirrors the Java driver, with Neo4j::Driver::GraphDatabase.driver(...) as the entry point. A higher-level OGM (neo4j-core) provides ActiveRecord-style node definitions.

Key Differentiators

Protocol: Bolt vs HTTP

Neo4j drivers communicate over Bolt — a compact, binary, length-prefixed protocol designed for graph data. The official drivers dropped HTTP support after the 4.x series. If you need HTTP, you must use a community driver (PHP, Perl, or the now-legacy neo4j-http clients).

The Bolt protocol is version-negotiated: the driver and server agree on the highest mutually-supported version during handshake. Neo4j 2025.x and the 6.x driver line support Bolt 6.0, which adds the Vector type and improved error codes.

Type Handling

Graph databases return a richer type system than SQL row mappers, and each language handles this differently:

Neo4j TypePythonJavaScriptJavaGo
Integer (int64)intneo4j.Integerlongint64
Floatfloatnumberdoublefloat64
StringstrstringStringstring
ListlistArrayList[]any
MapdictobjectMapmap[string]any
Nodeneo4j.Nodeneo4j.types.NodeNodeneo4j.Node
Point (2D/3D)neo4j.spatial.Pointneo4j.types.PointPointneo4j.Point
Durationneo4j.Durationneo4j.types.DurationIsoDurationneo4j.Duration
Vector (6.x+)list[float]neo4j.VectorN/AN/A

The JavaScript driver requires explicit integer handling: record.get("count").toNumber() or risk precision loss. The Python driver auto-converts to Python int (which is arbitrary precision, so lossless). The Go driver uses int64 and will silently truncate values exceeding that range.

Transaction Models

All official drivers support three transaction patterns, though the ergonomics differ:

  1. Auto-commit transactions — a single session.run("QUERY"). Simple, no retry.
  2. Explicit transactionssession.beginTransaction(), manual tx.commit() / tx.rollback(). Full control.
  3. Transaction functionssession.executeRead(tx => ...). The driver handles retry on transient errors. This is the recommended pattern for production code.
# Transaction function (Python) — automatic retry on transient errors
def get_repositories(tx):
    result = tx.run("MATCH (n:Repository) RETURN n.name AS name LIMIT 10")
    return [record["name"] for record in result]

with driver.session() as session:
    names = session.execute_read(get_repositories)

Connection Pooling and Routing

Every official driver maintains a connection pool internally. The pool is per-driver-instance, sized per configuration (defaults vary: Python defauts to 100 connections, JavaScript to 100, Go to 87).

When using the neo4j:// URI scheme, the driver performs client-side routing: it queries the server for cluster topology, then directs read transactions to followers and write transactions to the leader. This is transparent to application code — you call session.executeRead and the driver picks the right server.

Decision Matrix

CriterionPythonJavaScriptJavaGo.NETRust (neo4rs)PHP
StatusOfficialOfficialOfficialOfficialOfficialCommunityCommunity
Latest ver.6.2.05.28.3 / 6.0.15.28.06.0.05.x0.8.05.x
Bolt 6.0✓ (6.x)
Cluster routing
Async✓ (async def)✓ (native)✓ (CF + Rx)✓ (ctx)✓ (Task)✓ (tokio)
Reactive streamsExperimental✓ (RxJS)
Vector type✓ (6.x)✓ (6.x)
Browser bundle✓ (WebSocket)
Min version3.10Node 18Java 8Go 1.24.NET 6Rust 1.7xPHP 8.1
TestKit-passing

Which Driver Should You Use?

  • Python if you're building data pipelines, ML workflows, or LLM integrations — the Python driver has the richest Bolt 6.0 support and optional Rust extensions for throughput.
  • JavaScript if your app runs in the browser or needs WebSocket connectivity — it's the only driver with a production browser bundle.
  • Java if you're in the Spring ecosystem — Spring Data Neo4j provides annotation-driven mapping that no other language matches.
  • Go if you need maximum goroutine concurrency — Go's context.Context integration and connection pooling excel in high-throughput microservices.
  • PHP if you need HTTP protocol support at all — it's the only actively maintained driver with a working HTTP transport, and it passes the official TestKit.
  • Rust if you're willing to accept trade-offs — neo4rs is stable but stuck on Bolt 4.3; the other Rust driver has better protocol support but is a single-person project.

The landscape is remarkably consistent across the five official drivers: the same session-transaction patterns, the same URI schemes (bolt://, neo4j://, +s for TLS), and the same connection lifecycle. Pick the driver that fits your language, not the one with the most features — they all speak the same Bolt protocol underneath.