$npx -y skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-python-skillNeo4j Python Driver v6 — driver lifecycle, execute_query, managed and explicit
| 1 | ## When to Use |
| 2 | - Writing Python code that connects to Neo4j |
| 3 | - Setting up driver, sessions, transactions, or async patterns |
| 4 | - Debugging result handling, serialization, or UNWIND batching |
| 5 | - Reviewing Neo4j driver usage in Python code |
| 6 | |
| 7 | ## When NOT to Use |
| 8 | - **Writing/optimizing Cypher** → `neo4j-cypher-skill` |
| 9 | - **Driver version upgrades** → `neo4j-migration-skill` |
| 10 | - **GraphRAG pipelines** (`neo4j-graphrag` package) → `neo4j-graphrag-skill` |
| 11 | |
| 12 | --- |
| 13 | |
| 14 | ## Installation |
| 15 | |
| 16 | ```bash |
| 17 | pip install neo4j # package name is `neo4j`, NOT `neo4j-driver` (deprecated since v6) |
| 18 | pip install neo4j-rust-ext # optional: 3–10× faster serialization, same API |
| 19 | ``` |
| 20 | |
| 21 | **Python >=3.10 required** for v6.x. Python 3.14 supported [6.1+]. Pandas 3 and PyArrow 23/24 supported [6.2+]. |
| 22 | |
| 23 | --- |
| 24 | |
| 25 | ## Environment Variables |
| 26 | |
| 27 | Load connection config from environment — never hardcode credentials. |
| 28 | |
| 29 | ```python |
| 30 | import os |
| 31 | from dotenv import load_dotenv # pip install python-dotenv |
| 32 | |
| 33 | load_dotenv(".env") # reads NEO4J_URI / NEO4J_USERNAME / NEO4J_PASSWORD / NEO4J_DATABASE |
| 34 | |
| 35 | URI = os.getenv("NEO4J_URI", "neo4j://localhost:7687") |
| 36 | USER = os.getenv("NEO4J_USERNAME", "neo4j") |
| 37 | PASSWORD = os.getenv("NEO4J_PASSWORD", "") |
| 38 | DATABASE = os.getenv("NEO4J_DATABASE", "neo4j") |
| 39 | ``` |
| 40 | |
| 41 | `.env` file format: |
| 42 | ``` |
| 43 | NEO4J_URI=neo4j+s://xxx.databases.neo4j.io |
| 44 | NEO4J_USERNAME=neo4j |
| 45 | NEO4J_PASSWORD=secret |
| 46 | NEO4J_DATABASE=neo4j |
| 47 | ``` |
| 48 | |
| 49 | Add `.env` to `.gitignore`. Without `python-dotenv`, use `export` in shell or `os.getenv` directly. |
| 50 | |
| 51 | --- |
| 52 | |
| 53 | ## Driver Lifecycle |
| 54 | |
| 55 | Create **one Driver per application**. Thread-safe, expensive to create. Never create per-request. |
| 56 | |
| 57 | ```python |
| 58 | from neo4j import GraphDatabase |
| 59 | |
| 60 | URI = "neo4j+s://xxx.databases.neo4j.io" # Aura |
| 61 | AUTH = ("neo4j", "password") |
| 62 | |
| 63 | # Context manager — preferred for scripts |
| 64 | with GraphDatabase.driver(URI, auth=AUTH) as driver: |
| 65 | driver.verify_connectivity() |
| 66 | # ... work ... |
| 67 | |
| 68 | # Long-lived singleton (service / web app) |
| 69 | driver = GraphDatabase.driver(URI, auth=AUTH) |
| 70 | driver.verify_connectivity() |
| 71 | # on shutdown: |
| 72 | driver.close() |
| 73 | ``` |
| 74 | |
| 75 | URI schemes: |
| 76 | |
| 77 | | Scheme | Use | |
| 78 | |---|---| |
| 79 | | `neo4j+s://` | TLS + cluster routing — **Aura default** | |
| 80 | | `neo4j://` | Unencrypted + cluster routing | |
| 81 | | `bolt+s://` | TLS, single instance | |
| 82 | | `bolt://` | Unencrypted, single instance | |
| 83 | |
| 84 | Auth options: `("user", "pass")` tuple, `basic_auth()`, `bearer_auth("jwt")`, `kerberos_auth("b64")`. |
| 85 | |
| 86 | --- |
| 87 | |
| 88 | ## Choosing the Right API |
| 89 | |
| 90 | | API | Use when | Auto-retry | Streaming | |
| 91 | |---|---|---|---| |
| 92 | | `driver.execute_query()` | Most queries — simple, safe default | ✅ | ❌ eager | |
| 93 | | `session.execute_read/write()` | Large results / multiple queries in one tx | ✅ | ✅ | |
| 94 | | `session.run()` | `LOAD CSV`, `CALL {} IN TRANSACTIONS`, scripts | ⚠️ one-shot [6.2+] | ✅ | |
| 95 | | `AsyncGraphDatabase` | asyncio applications | ✅ | ✅ | |
| 96 | |
| 97 | `session.run()` retry [6.2+]: single immediate retry on DBMS-marked idempotent errors only (currently admission control). Disable with `disable_auto_commit_retries=True` at driver or session level. |
| 98 | |
| 99 | --- |
| 100 | |
| 101 | ## `execute_query` — Default API |
| 102 | |
| 103 | ```python |
| 104 | from neo4j import GraphDatabase, RoutingControl |
| 105 | |
| 106 | # Tuple unpacking — most common |
| 107 | records, summary, keys = driver.execute_query( |
| 108 | "MATCH (p:Person {name: $name})-[:KNOWS]->(f) RETURN f.name AS name", |
| 109 | name="Alice", |
| 110 | routing_=RoutingControl.READ, # route reads to replicas |
| 111 | database_="neo4j", # always specify — saves a round-trip |
| 112 | ) |
| 113 | for record in records: |
| 114 | print(record["name"]) |
| 115 | print(summary.result_available_after, "ms") |
| 116 | |
| 117 | # Write — check counters |
| 118 | summary = driver.execute_query( |
| 119 | "CREATE (p:Person {name: $name, age: $age})", |
| 120 | name="Bob", age=30, |
| 121 | database_="neo4j", |
| 122 | ).summary |
| 123 | print(summary.counters.nodes_created) |
| 124 | ``` |
| 125 | |
| 126 | **Trailing-underscore convention** — config kwargs end with `_` (`database_`, `routing_`, `auth_`, `result_transformer_`, `bookmark_manager_`). No query parameter name may end with `_`; pass those via `parameters_={"key_": val}`. |
| 127 | |
| 128 | **Never f-string or format Cypher.** Always `$param` — prevents injection and enables plan caching. |
| 129 | |
| 130 | `result_transformer_` — reshape before return: |
| 131 | ```python |
| 132 | import neo4j |
| 133 | df = driver.execute_query("MATCH (p:Person) RETURN |