Chapter 13 · Application Drivers and Bolt: Sessions, Routing, Parameters, Result Streaming, and Connection Pools

Official Drivers, Bolt Protocol Concepts, URI Schemes, Authentication, TLS, and Routing Tables

Treat the official driver as a long-lived protocol, security, pooling and routing boundary—not as a disposable query helper.

Intermediate150–190 minutesBolt/TLS/routing lifecycle labNeo4j 2026.07.1 Community · Cypher 25Python driver 6.3.0 · Bolt 6/5 awareLast reviewed: September 2026

Learning outcomes

AtlasMart now has Python services calling the graph. A database query can be correct in Browser and still fail in production because the client chooses the wrong URI, opens a new driver per request, negotiates TLS incorrectly, or bypasses cluster routing. This lesson treats the driver as a long-lived networking component rather than a convenience wrapper around Cypher.

01

Explain the responsibilities of an official Neo4j driver and the negotiated Bolt protocol.

02

Choose between bolt/neo4j URI schemes and their +s/+ssc TLS variants.

03

Separate driver construction, authentication, TLS verification, connectivity verification, and actual connection creation.

04

Distinguish direct-address connections from routing-enabled cluster/Aura connections.

05

Prove client/server compatibility without relying on undocumented driver internals.

Chapter 13 baseline · reviewed 9 September 2026

The mandatory lab continues Neo4j Community 2026.07.1, database neo4j, explicit CYPHER 25 where query-language version matters, container atlasmart-neo4j, loopback Bolt 127.0.0.1:7687, authentication neo4j/atlasmart-course-2026, and no TLS on the loopback-only disposable instance. The current official Python driver is neo4j 6.3.0 (Python 3.10–3.14), whose API supports Bolt 6.0–6.1, Bolt 5.0–5.8 and Bolt 4.4. Neo4j 5.26.30 remains the LTS comparison line.

Evidence boundary

This generation environment does not connect to the AtlasMart Neo4j container, so no handshake, routing-table, pool, TLS, bookmark, latency or retry output is fabricated. Each exercise gives commands and invariants to capture on your machine. True multi-member read/write routing requires an Enterprise cluster or Aura deployment; the mandatory Community path proves the driver/session/pool/stream semantics locally and labels cluster-only observations separately.

1. The driver is a protocol client with a pool

Layer Responsibility Observable evidence
Application repository/service Own query text, parameters, transaction boundary and API contract request ID, business result, structured error
Official driver Pool connections, negotiate Bolt, encode values, stream records, route/retry where applicable driver debug logs, summaries, exceptions, bookmarks
Bolt Binary application protocol between driver and Neo4j handshake/protocol debug logs; default server port 7687
Neo4j server Authenticate, plan/execute Cypher, enforce constraints/transactions query/transaction listings, server logs, result summary

The current Python driver is 6.3.0. Its documented protocol range includes Bolt 6.0–6.1, 5.0–5.8 and 4.4. The current Neo4j 2026 line can negotiate Bolt 6.0 and compatible 5.x versions. Applications should use a supported official driver and let the handshake select a mutually supported protocol rather than hard-coding a Bolt message version.

2. URI schemes encode routing and TLS policy

URI Routing discovery Encryption / certificate trust Typical use
bolt://host:7687 No; direct address Unencrypted unless custom driver encryption config Loopback lab or deliberate direct-member access
bolt+s://host:7687 No TLS; CA-signed certificate with full checks Direct secure endpoint
bolt+ssc://host:7687 No TLS; accepts self-signed certificate Controlled dev/private PKI scenarios; not a shortcut for public production trust
neo4j://host:7687 Yes Unencrypted unless custom encryption config Routing-enabled self-managed cluster when TLS is separately addressed
neo4j+s://host:7687 Yes TLS + CA validation Aura default and secure routing endpoints
neo4j+ssc://host:7687 Yes TLS + self-signed acceptance Controlled routing deployments with intentional self-signed trust

A neo4j:// driver obtains and refreshes routing information; a bolt:// driver connects to the named address and does not use routing discovery. On the mandatory Community lab there is no multi-member cluster, so routing-role behavior is explained and, if desired, observed separately on Aura or an Enterprise cluster.

3. Build one driver and verify the endpoint

PowerShell · create the Python environment
py -3 -m venv .venv.\.venv\Scripts\Activate.ps1python -m pip install --upgrade pippython -m pip install neo4j==6.3.0python -c "import neo4j; print(neo4j.__version__)"
Python · one application-lifetime driver
from __future__ import annotationsimport osfrom neo4j import GraphDatabase, RoutingControlclass AtlasMartNeo4j:    def __init__(self) -> None:        self.database = os.getenv("NEO4J_DATABASE", "neo4j")        self.driver = GraphDatabase.driver(            os.getenv("NEO4J_URI", "bolt://127.0.0.1:7687"),            auth=(os.getenv("NEO4J_USER", "neo4j"),                  os.getenv("NEO4J_PASSWORD", "atlasmart-course-2026")),            max_connection_pool_size=20,            connection_acquisition_timeout=5.0,            connection_timeout=5.0,            max_connection_lifetime=1800.0,            max_transaction_retry_time=15.0,        )    def verify(self) -> None:        self.driver.verify_connectivity()    def close(self) -> None:        self.driver.close()    def customer_orders(self, customer_id: str) -> list[dict]:        records, summary, _ = self.driver.execute_query(            """            CYPHER 25            MATCH (:Customer {customerId:$customerId})-[:PLACED]->(o:Order)            RETURN o.orderId AS orderId, o.status AS status, o.orderedAt AS orderedAt            ORDER BY o.orderedAt DESC, o.orderId            """,            customerId=customer_id,            database_=self.database,            routing_=RoutingControl.READ,        )        return [r.data() for r in records]
Python · startup probe and server info
from atlasmart_neo4j import AtlasMartNeo4jstore = AtlasMartNeo4j()try:    store.verify()    info = store.driver.get_server_info()    print("agent:", info.agent)    print("address:", info.address)    print("protocol:", info.protocol_version)finally:    store.close()

GraphDatabase.driver(...) configures a driver but connection establishment is lazy. verify_connectivity() forces an actual compatibility/authentication/connectivity check. get_server_info() is documented evidence for the negotiated server/protocol; do not parse debug log strings as an API contract.

4. Failure injection: prove that TLS/auth are separate failures

PowerShell · wrong password should fail without changing data
$env:NEO4J_URI='bolt://127.0.0.1:7687'$env:NEO4J_USER='neo4j'$env:NEO4J_PASSWORD='definitely-wrong'python .\check_connection.py
PowerShell · secure URI against non-TLS local endpoint should fail
$env:NEO4J_URI='bolt+s://127.0.0.1:7687'$env:NEO4J_PASSWORD='atlasmart-course-2026'python .\check_connection.py
Interpretation

An authentication error proves the server was reachable far enough to reject credentials. A TLS/certificate error proves something different. Do not collapse every startup failure into “database unavailable,” and never log raw passwords or tokens while diagnosing it.

5. Wrong approach: one driver per HTTP request

Python · deliberately wrong lifecycle
def handle_request(customer_id):    driver = GraphDatabase.driver(URI, auth=AUTH)  # WRONG: expensive pool owner per request    try:        return driver.execute_query(            "MATCH (c:Customer {customerId:$id}) RETURN c.name",            id=customer_id, database_="neo4j")    finally:        driver.close()

This churns pools, TCP/TLS handshakes and authentication work, and makes connection budgets unpredictable. The repair is one maintained Driver per application/process configuration, with short-lived sessions or execute_query() calls borrowing from its pool.

6. Observable routing without depending on private fields

Python · debug-only network/pool logging
from neo4j.debug import Watcherwith Watcher("neo4j.io", "neo4j.pool"):    store.verify()    print(store.customer_orders("C-1001"))

The exact debug text is explicitly not API-stable, so use it only to understand handshakes, connection borrowing and routing refresh. In production telemetry, prefer your own request/transaction metadata, summaries and structured metrics over scraping driver debug messages.

Production judgment

Decision Evidence to collect Risk if guessed
URI/routing deployment topology and failover target pinning a single member or sending all reads to writers
TLS scheme certificate chain, hostname, rotation process plaintext traffic or insecure certificate trust
driver version server/driver compatibility matrix and upgrade tests protocol or API incompatibility
driver lifetime connection creation rate and pool metrics churn, tail-latency spikes, exhausted sockets
auth rotation credential/token lifetime and rollout test outages during secret rotation

Check your understanding

  1. Why is neo4j+s:// not equivalent to bolt+s://?
  2. Is a Driver created with GraphDatabase.driver() proof that the server is reachable?
  3. Why keep one driver for the process?
  4. Can Community reproduce a true primary/secondary routing table?
  5. What does a negotiated Bolt version prove?
Review the answers

1. Both enable TLS with CA verification, but neo4j+s also enables routing discovery; bolt+s addresses one server directly.

2. No. Connections are lazy; use verify_connectivity() or execute work.

3. The driver is thread-safe, expensive to create, and owns the reusable connection pool.

4. No. Multi-member clustered routing is an Enterprise/Aura topology concern; Community can still learn the URI and driver semantics.

5. Only that client and server found a mutually supported protocol; it does not prove application query correctness or performance.

Summary and next step

Driver construction establishes the networking policy; sessions and results establish the unit-of-work and backpressure policy. Lesson 2 follows a query from a short-lived session through a lazy result stream.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.