Neo4j customized code

Neo4j allows you to extend its functionality by writing your own code using user-defined procedures and functions. They can be invoked directly from Cypher and are the preferred way of extending Neo4j.

Examples of use cases for procedures and functions are:

  • To provide access to functionality that is not available in Cypher.

  • To provide access to third-party systems.

  • To perform graph-global operations, such as counting connected components or finding dense nodes.

  • To express a procedural operation that is difficult to express declaratively with Cypher.

Writing user-defined procedures and functions

Procedures and functions should be implemented in a language within the JVM ecosystem and packaged into a JAR file. See Create a procedure or function for details.

Deploying user-defined procedures and functions

Once compiled, these JAR files are deployed to the NEO4J_HOME/plugins directory on each standalone or clustered server. For the location of the plugins directory, refer to Operations Manual → Default file locations.

Introduced in 2026.09 Enterprise Only You can use the built-in procedure dbms.reloadProcedures() to reload procedures and functions without restarting the DBMS. See Reloading procedures and functions for details.

Keep in mind that in a cluster deployment, you need to deploy the JAR files and run the dbms.reloadProcedures() procedure on each cluster member. Otherwise, the cluster members will be out of sync, leading to unexpected behavior.

The reload process is isolated so that the operation only affects new transactions. Already running transactions continue to execute with a snapshot of the available procedures or functions at their respective initialization.

Comparison of procedures and functions

Procedures and functions can take arguments and return results. In addition, procedures can perform write operations on the database.

Type Description Syntax Read/Write Cardinality

Procedure

For each row, the procedure takes parameters and returns multiple results.

CALL abc(...)

Update allowed.

Changes cardinality similarly to a MATCH clause (0, 1, or many).

Scalar function

For each row, the function takes parameters and returns a single result.

abc(...)

Read-only.

Maintains cardinality, one for one.

Aggregating function

Consumes many rows and produces an aggregated result.

WITH abc(...)

Read-only.

Reduces cardinality, many down to one.

Memory resource tracking

The memory resource tracking API for the procedure framework is available for preview. Future versions of Neo4j might contain breaking changes to this API.

If your procedure or function allocates significant amounts of heap memory, you can register allocations to count towards the configured transaction limits, see Operations Manual → Limit transaction memory usage for more information. This allows you to avoid OutOfMemory errors and terminate queries instead of causing uncontrollable memory consumption. Memory allocations also show up in query profiles.

To do this you need to inject org.neo4j.procedure.memory.ProcedureMemory as a field in your procedure/function class. ProcedureMemory has various methods to allow you to register allocations. For example (see javadocs for a full reference):

  • ProcedureMemoryTracker newTracker() creates a new memory resource tracker that is bound to the current transaction.

  • HeapEstimator heapEstimator() estimates the heap size of classes and instances.

  • HeapTrackingCollectionFactory collections() lets you create collections that have built-in memory tracking of their internal structure.

It’s usually difficult and time-consuming to implement memory resource tracking. These are a few considerations and caveats that are worth keeping in mind:

  • Limit the scope of the memory management. Focus only on parts that can grow significantly in memory and ignore minor underestimation.

  • Beware of overestimation by registering allocations of the same instance multiple times. You can add reference counting or other mechanisms to avoid overestimation if that is a concern.

  • It’s common not to know the size of an instance before it has been allocated, which may lead you to register allocations after they have already been made. The memory tracker implementation tries to prevent this by always pre-registering a certain amount of memory in the internal memory pools.

  • It’s cumbersome in Java to know when an instance has been garbage-collected. Typically, you register the release of memory at the point when it’s possible for that memory to be garbage-collected. To account for this, memory trackers may internally choose not to register the release of memory instantaneously.

  • Testing memory resource tracking can be difficult. One approach is to use a third-party library, like JAMM (Java Agent for Memory Measurements), and assert that the estimates are close enough for some given input.

A basic example of memory resource tracking in user defined procedures
package org.example;

import org.neo4j.procedure.Context;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;
import org.neo4j.procedure.memory.ProcedureMemory;

import java.util.Arrays;
import java.util.stream.Stream;

public class MyProcedures {

    @Context
    public ProcedureMemory memory;

    record Output(Long value) {}

    @Procedure("org.example.memoryHungryRange")
    public Stream<Output> memoryHungryRange(@Name("size") int size) {
        final var tracker = memory.newTracker();

        // Register the allocation of the long array below
        tracker.allocateHeap(memory.heapEstimator().sizeOfLongArray(size));
        // The actual allocation
        final var result = new long[size];

        for (int i = 0; i < size; i++) result[i] = i;

        return Arrays.stream(result)
                .mapToObj(Output::new)
                // Release all registered allocations when the stream is closed
                .onClose(tracker::close);
    }
}

Built-in procedures and functions

Neo4j also comes bundled with a number of built-in procedures and functions.

The available built-in procedures vary depending on edition and mode, as described in Operations Manual → Procedures. Running SHOW PROCEDURES displays the full list of procedures available in your Neo4j DBMS, including user-defined procedures.

The built-in functions are described in Cypher Manual → Functions. Running SHOW FUNCTIONS displays the full list of all the functions available in your Neo4j DBMS, including user-defined functions.