neo4j-driver-python-skill
Neo4j Python Driver v6 — driver lifecycle, execute_query, managed and explicit transactions, async (AsyncGraphDatabase), result handling, data type mapping, error handling, UNWIND batching, connection pool tuning, and causal consistency. Use when writing Python code that connects to Neo4j via GraphD
By neo4j-contrib · 647 installs
npx skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-python-skill
Source repository · Upstream listing
When to Use
Writing Python code that connects to Neo4j
Setting up driver, sessions, transactions, or async patterns
Debugging result handling, serialization, or UNWIND batching
Reviewing Neo4j driver usage in Python code
When NOT to Use
Writing/optimizing Cypher → neo4j cypher skill
Driver version upgrades → neo4j migration skill
GraphRAG pipelines ( neo4j graphrag package) → neo4j graphrag skill
Installation
Python =3.10 required for v6.x. Python 3.14 supported [6.1+]. Pandas 3 and PyArrow 23/24 supported [6.2+]. PyArrow 25 and Bolt 6.1 uuid.UUID values supported [6.3+]; driver created SSL contexts honour SSLKEYLOGFILE [6.3+]. Python 3.15 supported [6.4+]. Pin neo4j =6.4.1 — 6.4.0 can spin forever on pool acquisition and double encodes bearer tokens over HTTP Query API.
Neo4j 2026.08+ UUID properties require neo4j =6.3 to round trip as uuid.UUID . Storing UUID needs block store format (Enterprise); Community (aligned format) fails: storing properties of type UUID is not supported in aligned store format — store str(uuid) .
Environment Variables
Load connection config from environment — never hardcode credentials.
.env file format:
Add .env to .gitignore . Without python dotenv , use export in shell or os.getenv directly.
Driver Lifecycle
Create one Driver per application . Thread safe, expensive to create. Never create per request.
URI schemes:
Scheme Use
neo4j+s:// TLS + cluster routing — Aura default
neo4j:// Unencrypted + cluster routing
bolt+s:// TLS, single instance
bolt:// Unencrypted, single instance
https:// / http:// (e.g. https://host:7474/ ) HTTP Query API instead of Bolt [6.4+, preview] — pip install "neo4j[http]"
Auth options: ("user", "pass") tuple, basic auth() , bearer auth("jwt") , kerberos auth("b64") .
HTTP Query API [6.4+, preview — no semver guarantee]: pass database = / database= on every call (no home database resolution); auth basic (no realm) or bearer only. Ignored/unsupported: tx metadata + timeouts, fetch size (no backpressure), notification filters; VECTOR , UUID , UnsupportedType cannot be exchanged. Needs Bolt for any of these → use neo4j+s:// / bolt+s:// .
Choosing the Right API
API Use when Auto retry Streaming
driver.execute query() Most queries — simple, safe default ✅ ❌ eager
session.execute read/write() Large results / multiple queries in one tx ✅ ✅
session.run() LOAD CSV , CALL {} IN TRANSACTIONS , scripts ⚠️ one shot [6.2+] ✅
AsyncGraphDatabase asyncio applications ✅ ✅
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.
execute query — Default API
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} .
Never f string or format Cypher. Always $param — prevents injection and enables plan caching.
result transformer — reshape before return:
Result.single() defaults to strict=False : 0 rows → None ; 2+ rows → first record + warning (no exception). Only single(strict=True) raises ResultNotSingleError (0 or 2+). Use strict=True when exactly one row required, e.g. result transformer =lambda r: r.single(strict=True) ; else check for None .
Managed Transactions ( execute read / execute write )
Use for large results or multiple queries in one transaction.
Result lifetime — Result is a lazy cursor backed by the open transaction. Returning it unconsumed raises ResultConsumedError . Always collect to list inside the callback.
Callback may retry on transient failures — keep callbacks idempotent; move side effects (HTTP calls, emails) outside the callback.
Timeout/metadata via @unit of work (named functions only — cannot decorate lambdas):
Implicit Transactions ( session.run )
Use only for LOAD CSV , CALL {} IN TRANSACTIONS , or quick scripts. session.run() does a single immediate retry on idempotent (DBMS marked) errors only [6.2+]; other errors do not retry.
Async API
Mirror of sync API — replace GraphDatabase with AsyncGraphDatabase , await every call.
FastAPI lifespan pattern:
Parallel queries with asyncio.gather :
Never use sync GraphDatabase in asyncio — blocks the event loop.
Full async patterns → [references/async.md](references/async.md)
Error Handling
Catch ConstraintError before Neo4jError — it is a subclass and will be swallowed otherwise.
Result Access & Null Safety
record.data() : Node → dict of properties, Relationship → (start props, type, end props) tuple (own properties dropped), Path → list. json.dumps accepts these but loses labels, element IDs, relationship properties. neo4j.time.Date / Time / DateTime stay driver objects → json.dumps raises TypeError . Project needed scalars in Cypher ( toString() temporals); don't return whole entities.
Node/Relationship/temporal access:
Full type mapping table → [references/data types.md](references/data types.md)
Batch Writes with UNWIND
Pass list[dict] — only shape the driver serializes correctly for UNWIND .
Custom objects and dataclasses must be converted to dict before passing as parameters.
Performance
Always set database / database= — omitting triggers a home database round trip per call.
execute read routes to replicas automatically; use routing =RoutingControl.READ with execute query .
Batch writes: one execute write callback for the whole list one tx per item.
Large results: stream lazily inside execute read callback; execute query is always eager.
Connection pool tuning:
Session exhaustion: each open session holds a connection. Always use with driver.session(...) as session .
Full performance patterns → [references/performance.md](references/performance.md)
Common Errors
Mistake Fix
f string / .format() Cypher params Use $param placeholders always
Param name ending with Pass via parameters ={"key ": val}
Omitting database Always set — saves a round trip every call
Returning Result from tx callback Consume to list inside callback
Side effects in execute read/write callback Move outside — callback may retry
Passing dataclass/Pydantic as param Convert to dict first
UNWIND with list of objects list[dict] only
record.get() for absent key detection "key" in record.keys() for absent; .get() returns None for both absent and graph null
No .consume() after session.run() Commit timing undefined; call .consume()
Sync driver inside asyncio Use AsyncGraphDatabase — sync blocks event loop
Async driver created per request Singleton — create once at startup
Leaked sessions with driver.session(...) as session always
json.dumps(record.data()) with temporal values TypeError — toString() in Cypher or convert. Whole nodes/relationships serialize but lose labels, IDs, relationship properties — project scalars
result["name"] on EagerResult Index result.records[0]["name"] or unpack records, , = ...
Assuming Result.single() raises on 0 or 2+ rows Default strict=False : None (0 rows) or first record + warning (2+). single(strict=True) raises
@unit of work on lambda Use named function
Neo4jError caught before ConstraintError Catch ConstraintError first — it's a subclass
neo4j driver package name Package is neo4j since v6; neo4j driver deprecated
References
Load on demand:
[references/async.md](references/async.md) — full async patterns: managed transactions, result methods, concurrency
[references/data types.md](references/data types.md) — complete Python↔Cypher type mapping, temporal conversion, graph object API, spatial types (CartesianPoint/WGS84Point)
[references/performance.md](references/performance.md) — connection pool, lazy streaming, threading vs asyncio, bookmarks/causal consistency
[references/transactions.md](references/transactions.md) — explicit transactions, rollback, commit uncertainty, unit of work details
Docs:
https://neo4j.com/docs/python manual/current/
https://neo4j.com/docs/api/python driver/current/
Checklist
[ ] Package installed as neo4j (not neo4j driver )
[ ] One Driver instance created at startup; shared everywhere
[ ] verify connectivity() called at startup
[ ] database / database= set on every call
[ ] $param placeholders used — no f strings or .format()
[ ] Result consumed inside tx callback (not returned raw)
[ ] Sessions used as context managers ( with driver.session(...) as session )
[ ] ConstraintError caught before Neo4jError
[ ] AsyncGraphDatabase used in asyncio code (not sync driver)
[ ] Async driver created once at app startup (not per request)
[ ] Side effects outside execute read/write callbacks
[ ] UNWIND batches use list[dict]