Surface changes

This chapter describes breaking changes to the Neo4j surface when migrating from Neo4j version 3.5 to 4.0 and beyond.

1. Security

In version 4.x, the SSL framework has been reworked and the legacy SSL system has been deprecated. The following table outlines the security changes:

v3.x v4.x

The configuration settings and are used to disallow properties.

These configuration settings have been discontinued. The blocking functionality is replaced by the Cypher DENY command. Note that the DENY command must be applied while Neo4j is running.

For details, see Cypher Manual → Security → Graph and sub-graph access control.

This setting is replaced by two new settings: and

dbms.connector.https.enabled is set to true by default.

This setting is no longer true by default. To enable Neo4j to listen for incoming connections on the HTTPS port, you have to configure this setting to true.

The different communication channels are secured independently from each other, using the following configuration settings:

bolt.ssl_policy=<policy name>

https.ssl_policy=<policy name>

causal_clustering.ssl_policy=<policy name>

dbms.backup.ssl_policy=<policy name>

These settings have been replaced by the setting dbms.ssl.policy.<scope>.enabled=true, where <scope> substitutes the communication channel (bolt, https, cluster, and backup).

SSL support for Bolt and HTTPS using the legacy SSL system. The dbms.directories.certificates setting is used to explicitly configure the directory that stores the private key and certificate files.

The legacy SSL system has been deprecated and the dbms.directories.certificates setting has been removed. It is recommended to use the standard SSL configuration.


This setting has been removed. Neo4j no longer automatically generates a self-signed certificate.

For further details on the SSL framework changes, see Operations Manual → SSL framework

2. Changes to other configuration settings

Previous name Change New name (if applicable)














This is no longer a boolean setting. Valid values are: OFF, INFO or VERBOSE.





This setting has been renamed, and valid values are: DEBUG,INFO, WARN, ERROR or NONE



This setting is removed. Set causal_clustering.middleware.logging.level=OFF to disable middleware logging.





This setting no longer changes the default values of the individual metrics. Instead it turns off the whole metrics module.

3. Removal of REST API

The REST API has been removed in Neo4j 4.0. Cypher and procedures should be used instead, either via the HTTP API, or via Bolt using the official drivers.

The following HTTP endpoints were deprecated in Neo4j 3.4 and have now been removed:

HTTP endpoints












4. HTTP API endpoints

The HTTP API endpoints have been updated to accommodate multi database features. For example, the URI to begin a transaction has changed from: http://localhost:7474/db/data/transaction to: http://localhost:33471/db/neo4j/tx.

More generally, the HTTP API endpoints follow the pattern: http://localhost:33471/db/<database_name>/tx.

5. Cypher syntax

  • All changes in the Cypher language syntax are detailed in Cypher Manual → Removals deprecations additions and extensions. Please review it thoroughly and make necessary changes in your code.

  • We would like to draw some extra attention to the fact that the parameter syntax {parameter} is completely removed and has been replaced by the syntax $parameter.

6. Database naming rules

With the introduction of multiple databases, the rules for naming a database have changed. For example, it is no longer possible to use an underscore in a database name. For a full list of naming rules, please see Operations Manual → Administrative commands.

7. Procedures

The following procedures have been refactored:

Old procedure New procedure Comment

db.awaitIndex (indexId :: INTEGER?, timeOutSeconds = 300 :: INTEGER?) :: VOID

db.awaitIndex (indexName :: STRING?, timeOutSeconds = 300 :: INTEGER?) :: VOID

Indexes are now uniquely identified by name, instead of ID.

dbms.cluster.overview() :: (id :: STRING?, addresses :: LIST? OF STRING?, role :: STRING?, groups :: LIST? OF STRING?, database :: STRING?)

dbms.cluster.overview() :: (id :: STRING?, addresses :: LIST? OF STRING?, databases :: MAP?, groups :: LIST? OF STRING?)

Shows roles for all databases.

dbms.cluster.role() :: (role :: STRING?)

dbms.cluster.role (database :: STRING?) :: (role :: STRING?)

Takes database name as parameter.

dbms.cluster.routing.getRoutingTable(context :: MAP?) :: (ttl :: INTEGER?, servers :: LIST? OF MAP?)

dbms.cluster.routing.getRoutingTable (context :: MAP?, database = null :: STRING?) :: (ttl :: INTEGER?, servers :: LIST? OF MAP?)

Takes database name as parameter.

db.createIndex (index :: STRING?, providerName :: STRING?) :: (index :: STRING?, providerName :: STRING?, status :: STRING?)

db.createIndex (indexName :: STRING?, labels :: LIST? OF STRING?, properties :: LIST? OF STRING?, providerName :: STRING?, config = {} :: MAP?) :: (name :: STRING?, labels :: LIST? OF STRING?, properties :: LIST? OF STRING?, providerName :: STRING?, status :: STRING?)

Used to take the index pattern ":Label(prop)" as an argument, and now takes labels and properties as separate lists. Those are also yielded as result.

Now needs to be given an indexName.

Can now take index settings as a map. This is optional.

db.createUniquePropertyConstraint (index :: STRING?, providerName :: STRING?) :: (index :: STRING?, providerName :: STRING?, status :: STRING?)

db.createUniquePropertyConstraint (constraintName :: STRING?, labels :: LIST? OF STRING?, properties :: LIST? OF STRING?, providerName :: STRING?, config = {} :: MAP?) :: (name :: STRING?, labels :: LIST? OF STRING?, properties :: LIST? OF STRING?, providerName :: STRING?, status :: STRING?)

Used to take the index pattern ":Label(prop)" as an argument, and now takes labels and properties as separate lists. Those are also yielded as result.

Now needs to be given a constraintName.

Can now take index settings as a map. This is optional.

db.createNodeKey (index :: STRING?, providerName :: STRING?) :: (index :: STRING?, providerName :: STRING?, status :: STRING?)

db.createNodeKey (constraintName :: STRING?, labels :: LIST? OF STRING?, properties :: LIST? OF STRING?, providerName :: STRING?, config = {} :: MAP?) :: (name :: STRING?, labels :: LIST? OF STRING?, properties :: LIST? OF STRING?, providerName :: STRING?, status :: STRING?)

Used to take the index pattern ":Label(prop)" as an argument, and now takes labels and properties as separate lists. Those are also yielded as result.

Now need to be given a constraintName.

Can now take index settings as a map. This is optional.

db.indexes() :: (description :: STRING?, indexName :: STRING?, tokenNames :: LIST? OF STRING?, properties :: LIST? OF STRING?, state :: STRING?, type :: STRING?, progress :: FLOAT?, provider :: MAP?, id :: INTEGER?, failureMessage :: STRING?)

db.indexes() :: (id :: INTEGER?, name :: STRING?, state :: STRING?, populationPercent :: FLOAT?, uniqueness :: STRING?, type :: STRING?, entityType :: STRING?, labelsOrTypes :: LIST? OF STRING?, properties :: LIST? OF STRING?, provider :: STRING?)

Rename indexName to name.

Rename tokenNames to labelsOrTypes.

Rename progress to populationPercent.

Field type used to describe entity type (node or relationship), uniqueness, and index type. This splits up into type, uniqueness, and entityType.

Field provider is now a string instead of a map.

Removed description in favor of db.schemaStatements.

Moved failureMessage to procedure db.indexDetails.

db.resampleIndex (index :: STRING?) :: VOID

db.resampleIndex (indexName :: STRING?) :: VOID

Indexes are now uniquely identified by name, instead of index pattern ":Label(prop)".

The following are new procedures:

New procedure Comment

db.indexDetails (indexName :: STRING?) :: (id :: INTEGER?, name :: STRING?, state :: STRING?, populationPercent :: FLOAT?, uniqueness :: STRING?, type :: STRING?, entityType :: STRING?, labelsOrTypes :: LIST? OF STRING?, properties :: LIST? OF STRING?, provider :: STRING?, indexConfig :: MAP?, failureMessage :: STRING?)

For the specified index all information included by db.indexes together with indexConfig and failureMessage.

db.schemaStatements () :: (name :: STRING?, type :: STRING?, createStatement :: STRING?, dropStatement :: STRING?)

Get all create and drop statements needed to exactly replicate the schema rules (indexes and constraints) for this database.

This procedure can be used by client side tooling to test whether they are correctly connected to a database. The procedure is available in all databases and always returns true. A faulty connection can be detected by not being able to call this procedure.

8. Authentication and authorization

8.1. Deprecated and removed security procedures

In 3.x, authentication and authorization was managed via the built-in procedures. In 4.x, these procedures still exist but are deprecated. If you still want to use them, they must now be run in a session towards the system database, and cannot be followed by YIELD. There are two options for rewriting your code and routines for managing authentication and authorization. The first of these is recommended:

  1. Rewrite the procedures to the corresponding Cypher administration commands, using the the conversion guide below.

  2. Run the procedures in a session towards the system database and replace any YIELD parts by post-processing on the application side.

The procedure, requirePasswordChange) has been entirely removed since the corresponding Cypher administration command also requires the old password, and thus is more secure.

The following table is a conversion guide between the security procedures and the Cypher administration commands. For more info about the administration commands, see Cypher Manual → User and role management.

Procedure Administration command















8.2. Removal of flat files for authentication and authorization

In 3.x, authentication and authorization were managed in flat files. Users in the auth file and roles and role assignments in the roles file will be automatically migrated to the system database when upgrading from Neo4j 3.5 to Neo4j 4.0.

The Neo4j admin commands set-initial-password and set-default-admin continue to work in 4.0 and write to the same files as in 3.x. Any content in these files will be considered on the first start of Neo4j after upgrading from 3.5. You can run these commands before upgrading the Neo4j installation, or after, as long as they are run before completing the migration of the database files which is done at first start of the new installation.

The command set-initial-password will only be applied if the default user neo4j with the default password is the only user present, while set-default-admin will only be applied when no roles are present.

The use of auth and role files in Neo4j 3.x meant that multiple databases could have different user and role configurations. In addition, a single database configured in a cluster could have different auth and role settings on each instance of the cluster. Neo4j 4.0 allows multiple databases to run within a single instance, or in a cluster. If you are bringing multiple databases together from multiple Neo4j 3.5 installations, or if you are upgrading a cluster with multiple instances, you need to manually merge the auth and role files before the migration.

It is still possible to have different security configurations per database after the migration, but this needs to be managed through the granting of privileges and roles specific to databases after the migration. The built-in roles from 3.5 still exist, but will apply to all databases after the migration, unless explicitly modified using the new security administration commands. The ability to manage database specific roles and privileges is described in more detail in Cypher Manual → Administration.

It is no longer possible to have different security privileges on different instances of a cluster. The entire cluster shares the privileges configured in the system database using Cypher administration commands. In practice this means that users have the same privileges regardless of which server in a cluster they access.

9. Logs

Relevant logs produced by Neo4j will now have a prefix which indicates the database to which the log line pertains. Such log lines will have the database name printed prior to the regular text. For example, [neo4j] or [system].

Example 1. Some log lines for the system database
2019-12-02 22:27:41.820+0000 INFO [o.n.k.d.Database] [system] No check point found in transaction log
2019-12-02 22:27:41.820+0000 INFO [o.n.k.d.Database] [system] Recovery required from position LogPosition{logVersion=0, byteOffset=64}
2019-12-02 22:27:41.820+0000 INFO [o.n.k.r.Recovery] [system]   10% completed
2019-12-02 22:27:41.820+0000 INFO [o.n.k.r.Recovery] [system]   20% completed
2019-12-02 22:27:41.820+0000 INFO [o.n.k.r.Recovery] [system]   30% completed

Other log lines might relate to the DBMS as a whole, or be logged by a component that lives on a higher level but still operates on a particular database. For example:

Example 2. Some log lines from the Core database manager starting the Neo4j database.
2019-12-02 22:27:41.964+0000 INFO [c.n.c.c.CoreDatabaseManager] Creating 'neo4j' database.
2019-12-02 22:27:41.967+0000 INFO [c.n.c.c.CoreDatabaseManager] Starting 'neo4j' database.

10. Metrics

In 4.x, there are two types of metrics: global metrics and database-local metrics. The metric naming is different in 4.x compared to 3.x. For details about available metrics and the new naming patterns, please refer to Operations Manual → Metrics.

Please note that in 4.2 the metrics that are enabled by default have been changed.

Any specific metrics that you want to be enabled must be specified in the metrics.filter.

Additionally in the 4.2 release, metrics are no longer exposed via JMX by default. These can be enabled by adding metrics.jmx.enabled=true to Operations Manual → neo4j.conf.

11. Cluster discovery

Cluster discovery is now implemented on top of Akka, instead of Hazelcast, and a few minor changes have been made as part of this transition:

  • The discovery_advertised_address hostname and port must exactly match those configured for the discovery of other members.

    When discovery_type=LIST is used, this means that it is the list of addresses in initial_discovery_members which must match the respective advertised addresses of each server.

    When using any other discovery types (DNS, SRV, K8S), then it is the configuration in the external service which must match.

    Please note that by default your discovery_advertised_address is a combination of the default port assigned to that config, and the hostname assigned to default_advertised_address.
  • Connections are now opened from Cores to Read Replicas, in addition to vice versa, so therefore the advertised discovery port must be open on Read Replicas.

12. Cluster REST endpoints

The REST endpoints have moved and now exist per database:

Old endpoint New endpoint









13. JMX

In 3.x, Neo4j exposed several JMX MBeans in order to provide some monitoring information in addition to the metrics exposed by Neo4j. In some instances, the provided data was incomplete or incorrect, and in some cases different beans even provided conflicting information. All of the previous JMX endpoints (org.neo4j:*) have been removed and are replaced by a new set of beans (neo4j.metrics:*) that expose exactly the same information as the corresponding Neo4j metrics.

JMX MBeans are available only in Enterprise Edition.

14. Index migration

  • Indexes are automatically migrated to the most recent index provider during migration.

    Depending on what index providers were used previously, the migration of indexes may change the distribution of memory utilization. In a database with many indexes, a significant amount of memory may have been reserved for Lucene. After the migration, it could be necessary to allocate some of that memory to the page cache instead. For a detailed description on how memory is allocated and used, refer to Operations Manual → Memory configuration. Use neo4j-admin memrec --database to inspect the database before and after migration.

    Changes have been made to how large a key can be in a b-tree index. These changes are only relevant for indexes that use index provider lucene-1.0 or lucene+native-1.0 in 3.5, and hold large strings or large arrays. For a detailed description of this change, please refer to Operations Manual → Index migration.

  • Support for explicit indexes has been removed and the functionality has been replaced by full-text indexes. For details, see Cypher Manual → Indexes to support full-text search.

15. Tools

Database specific commands provided by neo4j-admin now support --database, which can be used to specify a database for a specified operation.

In cases when the --database option is not specified, neo4j will be used as the default database.

Also, there is a slight syntax change when adding options to neo4j-admin import. To add a label or relationship type to all nodes or relationships in an import file, the syntax is: neo4j-admin import --nodes=[<label>[:<label>]…​=]<files>…​ and neo4j-admin import --relationships=[<type>=]<files>…​]…​.

In addition, with the introduction of multiple databases, it is important to remember that if importing to a new database, it has to be explicitly created before the imported data can be accessed. For example, if the database is called importeddb, after data has been imported to it, create it with the following query:

:use system

16. Backups

Backups must now be taken of all databases.

A default installation has two databases, named system and neo4j respectively. Use the --database option of the neo4j-admin backup command to specify the database to backup. For more information, see Operations Manual → Back up an online database.

The --name parameter has been removed. It was previously used to specify the last part of the path when using --backup-dir. The last part of the path is now inferred from the --database parameter, which is used to specify the database name on the server. You are therefore no longer able to specify the last part of the path.

If you previously used --name for customizing the backup path, for example by including a timestamp, then an alternative is to now use --backup-dir instead.

17. Embedded layout

To support multiple databases in embedded, the store files, transaction files and log files no longer reside in the base directory. Instead, files are separated per database in separate directories.

18. Core Java API

18.1. JDK 11

Neo4j 4.0 is the first major release that requires JDK 11. Custom extensions and procedures can also be compiled now for JDK 11 (for example -target 11. It is generally recommended to use the latest available JDK 11 in order to access available fixes and leverage performance improvements.

18.2. Classes removed or excluded from the public API

Please refer to Classes removed from public API for a complete list of classes removed or excluded from the public API.

18.3. Renamed classes

The following classes have been renamed:

Old class name New class name





















18.4. Changes to the API


Neo4j 4.0 comes with significant changes in schema and indexes. Most of the related classes have additional possibilities. Changes include:

  • Starting with 4.0, all of the indexes are named. The name of an index can be retrieved using getName() call on IndexDefinition and ConstraintDefinition.

  • The definition of an index can be looked up by name using Schema.

  • Single label and relationship type accessors getLabel() and getRelationshipType() have been removed from IndexDefinition.

    Affected classes:

  • org.neo4j.graphdb.schema.ConstraintCreator

  • org.neo4j.graphdb.schema.ConstraintDefinition

  • org.neo4j.graphdb.schema.IndexCreator

  • org.neo4j.graphdb.schema.IndexDefinition

  • org.neo4j.graphdb.schema.Schema


Transaction event listeners have an updated behavior. Changes include:

  • As part of the callback, you will always receive the owning GraphDatabaseService as one of the parameters.

  • The beforeCommit listener method has access to an ongoing transaction over the transaction call parameter.

  • DatabaseEventListener is a new type of listener that has been introduced. Since Neo4j now supports multiple databases you might want to be able to listen to database events from several databases. It can be registered and de-registered in DatabaseManagementService.

    Affected classes:

  • org.neo4j.graphdb.event.TransactionEventListener

  • org.neo4j.graphdb.event.DatabaseEventContext

  • org.neo4j.graphdb.event.DatabaseEventListener


Most of the helpers are no longer part of the public API. The SocketAddress helper has minor API changes.

Affected classes:

  • org.neo4j.configuration.helpers.SocketAddress


The backup facade has been simplified and adapted to a multi-database environment.

Affected classes:

  • com.neo4j.backup.OnlineBackup


Configuration API has been updated to be typed. It is no longer safe to assume that the configuration is a set of random key-value pairs. All pairs unknown to Neo4j will be rejected. Additionally, some settings have been renamed as well. Please check settings names migration in the corresponding migration manual section.

Affected classes:

  • org.neo4j.configuration.GraphDatabaseSettings

  • org.neo4j.graphdb.config.Setting

  • org.neo4j.configuration.connectors.BoltConnector


Transaction API changes are one of the biggest API updates that are part of 4.0. All of the methods that should be executed in transaction have been moved from GraphDatabaseService to Transaction. This means that if you need to create entities, or access them, you should now be able to find all of the methods in Transaction. Additionally, starting with 4.0, transactions are no longer thread-bound. This means that any call to GraphDatabaseService::beginTx() will create a new independent transaction, even if it called from one thread.

Affected classes:

  • org.neo4j.graphdb.Transaction


Starting with 4.0, the PropertyContainer interface is removed, and all property-related methods moved to Entity. Access to entities should always be transactional. This also means that an entity can only be safely accessed from a transaction where it was created or retrieved.

Affected classes:

  • org.neo4j.graphdb.Entity

  • org.neo4j.graphdb.Node

  • org.neo4j.graphdb.Relationship


Starting with 4.0, all methods that require transactions are moved to Transaction. In addition, a set of executeTransactionally methods have been added to provide a convenient way of query executions in a separate transaction.

Affected classes:

  • org.neo4j.graphdb.GraphDatabaseService

org.neo4j.harness and com.neo4j.harness

Support has been added for official testing support classes. Starting with 4.0, Neo4j provides a set of Junit 4 rules and Junit 5 extensions for community and enterprise users.

Affected classes:

  • com.neo4j.harness.junit.extension.EnterpriseNeo4jExtension

  • com.neo4j.harness.junit.rule.EnterpriseNeo4jRule

  • org.neo4j.harness.junit.extension.Neo4j

  • org.neo4j.harness.junit.extension.Neo4jExtension

  • org.neo4j.harness.junit.extension.Neo4jExtensionBuilder

  • org.neo4j.harness.junit.rule.Neo4jRule


The top-level Neo4j API has been updated. The main access point that should be used to access individual databases, or perform any database management operations, is called DatabaseManagementService. It can be constructed by the Community or Enterprise version of DatabaseManagementServiceBuilder.

Example 3. Using DatabaseManagementService

In this example, we are constructing a new managementService and a lookup GraphDatabaseService for the database named neo4j:

var managementService = new DatabaseManagementServiceBuilder( homeDirectory ).build();
var databaseService = managementService.database( "neo4j" );

Affected classes:

  • org.neo4j.dbms.api.DatabaseManagementService

  • org.neo4j.dbms.api.DatabaseManagementServiceBuilder