Run non-blocking asynchronous queries

The examples in Query the database and Run your own transactions use the driver in its synchronous form. This means that, when running a query against the database, your application waits for the server to retrieve all the results and transmit them to the driver. This is not a problem for most use cases, but for queries that have a long processing time or a large result set, asynchronous handling may speed up your application.

Asynchronous managed transactions

You run an asynchronous transaction through an AsyncSession. The flow is similar to that of regular transactions, except that async transaction functions return a CompletionStage object (which may be further converted into CompletableFuture).

package demo;

import java.util.Map;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import org.neo4j.driver.async.AsyncSession;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.summary.ResultSummary;
import org.neo4j.driver.SessionConfig;

public class App {

    public static void main(String... args) throws ExecutionException, InterruptedException {
        final String dbUri = "<URI for Neo4j database>";
        final String dbUser = "<Username>";
        final String dbPassword = "<Password>";

        try (var driver = GraphDatabase.driver(dbUri, AuthTokens.basic(dbUser, dbPassword))) {  (1)

            var summary = printAllPeople(driver);
            // Block as long as necessary (for demonstration purposes)
            System.out.println(summary.get());  (8)

    public static CompletableFuture<ResultSummary> printAllPeople(Driver driver) {
        var session = driver.session(AsyncSession.class, SessionConfig.builder().withDatabase("neo4j").build());  (2)

        var query = """
        UNWIND ['Alice', 'Bob', 'Sofia', 'Charles'] AS name
        MERGE (p:Person {name: name}) RETURN
        var summary = session.executeWriteAsync(tx -> tx.runAsync(query)  (3)
                .thenCompose(resCursor -> resCursor.forEachAsync(record -> {  (4)
            .whenComplete((result, error) -> {  (5)
            .toCompletableFuture();  (6)

        return summary;  (7)
1 Driver creation is the same in the synchronous and asynchronous versions.
2 An asynchronous session is created by providing AsyncSession.class as first parameter to Driver.session(), which returns an AsyncSession object. Note that async sessions may not be opened as resources with try statements, as the driver can’t know when it is safe to close them.
3 As for regular transactions, .executeWriteAsync() (and executeReadAsync()) take a transaction function callback. Inside the transaction function, run queries with .runAsync(). Each query run returns a CompletionStage.
4 Optionally use methods on CompletionStage to process the result in the asynchronous runner. The query’s result set is available as an AsyncResultCursor, which implements a similar set of methods for processing the result to those of synchronous transactions (see Transactions → Process query results).
Inner object types are the same as the synchronous case (i.e. Record, ResultSummary).
5 Optionally run operations once the query has completed, such as closing the driver session.
6 CompletableFuture is a convenient type to return back to the caller.
7 Contrary to synchronous transactions, .executeWriteAsync() and executeReadAsync() return the result summary only. Result processing and handling must be done inside the asynchronous runner.
8 .get() waits as long as necessary for the future to complete, and then returns its result.
The methods .executeReadAsync() and .executeWriteAsync() have replaced .readTransactionAsync() and .writeTransactionAsync(), which are deprecated in version 5.x and will be removed in version 6.0.



A Long Term Support release is one guaranteed to be supported for a number of years. Neo4j 4.4 is LTS, and Neo4j 5 will also have an LTS version.


Aura is Neo4j’s fully managed cloud service. It comes with both free and paid plans.


Cypher is Neo4j’s graph query language that lets you retrieve data from the database. It is like SQL, but for graphs.


Awesome Procedures On Cypher (APOC) is a library of (many) functions that can not be easily expressed in Cypher itself.


Bolt is the protocol used for interaction between Neo4j instances and drivers. It listens on port 7687 by default.


Atomicity, Consistency, Isolation, Durability (ACID) are properties guaranteeing that database transactions are processed reliably. An ACID-compliant DBMS ensures that the data in the database remains accurate and consistent despite failures.

eventual consistency

A database is eventually consistent if it provides the guarantee that all cluster members will, at some point in time, store the latest version of the data.

causal consistency

A database is causally consistent if read and write queries are seen by every member of the cluster in the same order. This is stronger than eventual consistency.


The null marker is not a type but a placeholder for absence of value. For more information, see Cypher → Working with null.


A transaction is a unit of work that is either committed in its entirety or rolled back on failure. An example is a bank transfer: it involves multiple steps, but they must all succeed or be reverted, to avoid money being subtracted from one account but not added to the other.


Backpressure is a force opposing the flow of data. It ensures that the client is not being overwhelmed by data faster than it can handle.

transaction function

A transaction function is a callback executed by an executeRead or executeWrite call. The driver automatically re-executes the callback in case of server failure.


A Driver object holds the details required to establish connections with a Neo4j database.