Drivers
The Drivers domain tests the client-side lifecycle: how connections are pooled, how sessions and transactions are scoped, how routing and retries work, and how results are consumed safely.
Objects and lifetime
Create a Driver once per application per DBMS. It is thread-safe and owns a connection pool, so building one per request exhausts connections. Sessions are cheap, short-lived, single-threaded units of work scoped to one database. Transactions are the unit of atomicity and retry. Always send values as parameters ($name); string concatenation breaks plan caching and invites injection.
Routing and URIs
bolt:// connects directly to one server. neo4j:// (or neo4j+s:// with TLS) performs routing, discovers the cluster topology, and sends writes to the leader and reads to followers when the session's access mode is READ. Aura requires the neo4j+s scheme.
Worked example
Python: driver.execute_query("MATCH (p:Person {name:$name}) RETURN p.name AS name", name="Ada", database_="neo4j"). Reasoning: this helper opens a session, runs a managed transaction with automatic retry on transient errors, consumes the result, and closes the session. Managed transaction functions (execute_read/execute_write) retry on transient failures such as deadlocks and leader switches; explicit BEGIN ... COMMIT transactions do not retry for you, so production code should prefer the managed form.
Traps
Consuming results after the session closes - results stream lazily, so materialize them inside the session. Sharing a session across threads. Treating a bookmark as a timestamp; it is an opaque token, and you must pass it to the next session to get causal consistency. Skipping driver.close() on shutdown. Assuming every error is retried; only transient categories are. Also remember verify_connectivity() before relying on a driver in a health check.