In this article, we’ll cover some best practices for using Neo4j drivers in your application. This is based on the blog post Neo4j Driver Best Practices written by David Allen who is Senior Director of Developer Relations here at Neo4j and contains some examples based on use of the python driver.
Connect Using the neo4j+s:// scheme Whenever Possible
With Neo4j Aura instances supporting version 4 and above,
neo4j+s:// is usually your best bet for connecting. The
+s means that certificate validation will be performed, and that your connection will be secure.
Use Very Recent Versions of Neo4j Drivers
If you have an older driver make sure to upgrade it for best connectivity, at least to the 4.4 series of drivers or preferably version 5.x, which are fully backward compatible.
Verify Connectivity Before Issuing Queries
Normally, the Neo4j driver creates connections as needed and manages them in a pool; by verifying connectivity at the very beginning, it forces the driver to create a connection at that moment. If the address, username, or password is wrong, it will fail immediately. It can be helpful to validate connectivity before starting to run any Cypher queries, especially as if you did receive any errors in your queries, you have already ruled out it being the result of a connection error.
Create One Driver Instance Once and Hold Onto It
Driver objects in Neo4j contain connection pools which can be typically expensive to create, it may take up to a few seconds to create all of the necessary connections and establish connectivity. Best practice here should be to only create one driver instance per Neo4j DBMS, hold on to that and use it for everything.
This is important in environments when using serverless cloud functions, such as AWS Lambda, where if you created a driver instance every time your code runs, it will incur a performance penalty. You may also want to consider changing connectivity settings to reduce the number of connections created, in order to reduce cold startup time.
Drivers are generally heavyweight objects that expect to be reused many times, whilst Sessions on the other hand are cheap, you can create and close as many of them as you like. You should create a session to run a transaction and close it after it has been used. They should be considered disposable.
Use Explicit Transaction Functions
Neo4j drivers don’t parse your Cypher and with the Neo4j clusters using routed drivers, if you use a simple call with
session.run and Auto-commit transaction, you typically end up sending all of your queries (both reads and writes) to the leader of your cluster. Effectively, you won’t be using 2/3rds of your 3-member cluster because all queries are going to the same machine.
Another thing that is not so good about using
session.run and Auto-commit transactions is that you lose control over the commit behaviour. Sometimes you need to run multiple cypher queries which all commit at once or all fail (rather than committing one by one individually).
The better practice is to use an “explicit transaction function” in order to distinguish between writes (
session.write_transaction) and reads (
session.read_transaction). These tell a driver what sort of transaction it is and allow the driver can spread the work out among cluster members.
Use Query Parameters Wherever Possible
Try to use query parameters such as
$name for example, to pass a name variable into a Cypher query. You should use these parameters wherever you’re substituting data into your queries, and never use string concatenation.
Use of query parameters gives shows the database fewer query forms, allowing the database to compile fewer queries and make them run faster, rather than every new query being a new string the database hasn’t seen before.
Process Database Results Within Your Transaction Function
It is possible for a transaction to be interrupted by either the constituent parts of the AuraDB Instance temporarily losing their connections to one another or for the network connection between your application and the cluster to be lost prior to committing. If either of those scenarios were to happen, the driver would report an error.
Transaction functions can be used to handle any transient connection problems that occur between your application and your AuraDB Instance and you should configure the driver to handle retires for transaction functions.
If you use these practices, your graph code will be safer, more secure, more performant, and also more portable between graph environments, whether you’re running on a Neo4j Desktop install, an AuraDB Free database, or scaling all the way up to large AuraDB Enterprise clusters.