Applications talk to Neo4j through drivers, and using parameters correctly is essential for safety and performance.
Install the official driver with pip install neo4j. Create one driver per application (it manages a connection pool) and close it at shutdown:
driver = GraphDatabase.driver("neo4j://localhost:7687", auth=("neo4j", "secret"))The neo4j:// scheme enables cluster routing; neo4j+s:// adds TLS (used by Aura). The simplest API is driver.execute_query(query, params..., database_="neo4j"), which runs a query in a retried transaction and returns records, summary, keys. Each Record behaves like a dict and a tuple.
Always use parameters: write $name in Cypher and pass values separately. This prevents Cypher injection, handles quoting and types, and lets Neo4j cache the query plan; string-built queries are re-planned every time. Parameters can be scalars, lists or maps (SET p += $props, UNWIND $rows AS row).
Pass routing_=RoutingControl.READ for read-only queries so a cluster can serve them from secondaries. For multi-statement units of work, use a session with session.execute_write(fn) (managed transactions retried on transient errors), keeping the function free of side effects other than the queries.
Node values come back as neo4j.graph.Node objects with .labels, .element_id and dict-style property access.
Example
from neo4j import GraphDatabase, RoutingControl
URI, AUTH = "neo4j://localhost:7687", ("neo4j", "password")
with GraphDatabase.driver(URI, auth=AUTH) as driver:
driver.verify_connectivity()
driver.execute_query(
"MERGE (p:Person {name: $name}) SET p.born = $born",
name="Eve", born=1994, database_="neo4j")
records, summary, keys = driver.execute_query(
"""MATCH (p:Person) WHERE p.born >= $year
RETURN p.name AS name ORDER BY name""",
year=1990, database_="neo4j", routing_=RoutingControl.READ)
print([r["name"] for r in records]) # ['Ann', 'Cy', 'Eve']
print(summary.result_available_after, "ms") # e.g. 2 msRemember
- One driver per app; it pools connections
execute_queryruns a retried transaction and returns records, summary, keys- Use
$paramsfor values, never string formatting - Route reads with RoutingControl.READ
Watch out for
- Creating a new driver per request, which defeats connection pooling and is slow.