Chapter 12 · APOC, Procedures, Functions, Triggers-Like Workflows, and Extending Cypher
Custom Procedures/Functions Concepts, Java Extension Boundaries, Version Compatibility, and Deployment Risk
Treat custom Java procedures/functions as server deployment artifacts with compatibility, security, memory, test and rollback obligations—not merely alternate query syntax.
Learning outcomes
AtlasMart proposes a custom Java procedure because a transformation is “too slow in Cypher.” That may be valid, but a server-side JAR runs inside the Neo4j process and becomes part of the database deployment. The correct threshold is therefore much higher than “the code is easier to write in Java.”
Explain the API and lifecycle boundary of a custom procedure versus user-defined function.
Pin custom extension compilation/testing to the exact Neo4j server line.
Use Neo4j Harness and driver tests before deploying a JAR to a real server.
Identify heap, privilege, compatibility and observability risks of server-side Java code.
Compare a trigger/custom-procedure approach with pure Cypher and application services before deployment.
The mandatory lab continues Neo4j Community
2026.07.1, database neo4j, explicit
CYPHER 25 for version-sensitive examples,
container atlasmart-neo4j, authentication
enabled, loopback HTTP/Bolt endpoints, and the AtlasMart
identifiers/model built in Chapters 01–11. Neo4j
5.26.30 remains the LTS comparison line. This
chapter adds APOC Core 2026.07.1 as an optional
extension; APOC Extended is never required.
This generation environment does not run the Neo4j container, so no plugin load, procedure list, filesystem read/write, HTTP request, trigger execution or custom-JAR output is invented. The lab tells you exactly what to inspect. Community Edition lacks Enterprise RBAC/load privileges, so the free path emphasizes configuration minimization, loopback-only access, file/network denial by default, and application-level authorization boundaries.
1. Custom extensions run in the database JVM
| Extension | Call shape | Good fit | Risk |
|---|---|---|---|
| User-defined function | Expression returning a value | Pure deterministic computation that composes in Cypher | Bad implementation can consume CPU/heap in query execution |
| User-defined procedure |
CALL yielding rows/performing permitted work
|
Specialized server-side operation with clear transactional semantics | Row cardinality, writes, privileges and exception behavior become API contract |
| Unmanaged server extension | HTTP/JAX-RS surface | Rare cases needing custom HTTP integration on self-managed server | Sharp tool: arbitrary server code, heap/security surface, tight deployment coupling |
| Application service | Driver calls over Bolt | Business orchestration, external I/O, independent deployment | Network round trips and separate service operations |
Neo4j’s Java Reference explicitly treats these as advanced server/deployment mechanisms, not driver-side extensions.
2. Compile against the same release line
<properties> <maven.compiler.release>21</maven.compiler.release> <neo4j.version>2026.07.1</neo4j.version></properties><dependency> <groupId>org.neo4j</groupId> <artifactId>neo4j</artifactId> <version>${neo4j.version}</version> <scope>provided</scope></dependency><dependency> <groupId>org.neo4j.test</groupId> <artifactId>neo4j-harness</artifactId> <version>${neo4j.version}</version> <scope>test</scope></dependency>
The official current project template uses Neo4j
2026.07.1 and Java 21. Custom code should be
rebuilt and regression-tested during server upgrades because it
shares the server-side API/deployment lifecycle.
3. Minimal function: keep it pure and bounded
package academy.atlasmart;import org.neo4j.procedure.Name;import org.neo4j.procedure.UserFunction;public class AtlasMartFunctions { @UserFunction("atlasmart.normalizedSku") public String normalizedSku(@Name("value") String value) { if (value == null) return null; return value.trim().toUpperCase().replaceAll("[^A-Z0-9-]", ""); }}
This example is intentionally small. Before deploying it, define Unicode/locale semantics and test malicious/large inputs. A custom function does not become safe merely because it does not write the graph.
4. Test in Harness before copying a JAR into plugins
try (Neo4j neo4j = Neo4jBuilders.newInProcessBuilder() .withFunction(AtlasMartFunctions.class) .build()) { try (Driver driver = GraphDatabase.driver(neo4j.boltURI())) { try (Session session = driver.session()) { Record r = session.run( "RETURN atlasmart.normalizedSku($v) AS sku", Values.parameters("v", " sku-1001 ") ).single(); assertEquals("SKU-1001", r.get("sku").asString()); } }}
Test nulls, invalid inputs, large inputs, concurrency, exception mapping and Cypher 25 invocation. Then test the packaged JAR against a disposable server that matches production topology/configuration before rollout.
5. Deployment changes are database changes
| Step | Evidence |
|---|---|
| Build reproducibly | Pinned Maven dependencies, checksum/SBOM and clean CI build |
| Security review | No arbitrary file/network access; explicit privileges; dependency vulnerabilities reviewed |
| Compatibility test |
Harness + disposable Neo4j 2026.07.1 tests
pass
|
| Install |
JAR in $NEO4J_HOME/plugins; allowlist only
required namespace
|
| Restart | Server startup log shows extension load without errors |
| Verify |
SHOW FUNCTIONS/PROCEDURES returns only
intended names
|
| Rollback | Previous server/plugin pair and removal/restart procedure documented |
6. Deliberately wrong: move application orchestration into a custom procedure
A custom procedure that calls payment APIs, sends email and writes files turns a database transaction into a distributed side-effect orchestrator. Client timeouts and transaction retries become dangerous, external dependencies can hold locks open, and observability is harder. Keep external I/O in application/workflow services; use Neo4j transactions for graph state and durable outbox facts.
7. Trigger compatibility and upgrade coupling
APOC trigger procedures are another form of server-side
coupling. They are disabled by default via
apoc.trigger.enabled=false. Current Cypher 25 uses
apoc.trigger.install/drop/list; legacy
apoc.trigger.add is removed in Cypher 25. Treat
triggers as optional operational extensions, document every
installed trigger, and include them in upgrade and rollback
tests.
CYPHER 25CALL apoc.trigger.list()YIELD name, query, selectorRETURN name, query, selectorORDER BY name;
Check your understanding
- Why should custom procedures be rebuilt/tested during Neo4j upgrades?
- What does Neo4j Harness provide?
- Should a custom procedure perform remote payment/email calls inside a graph transaction?
-
Is
apoc.trigger.adda Cypher 25 API to build new code on? - What is the deployment rollback unit?
Review the answers
1. They run against server-side APIs and share the DBMS deployment/runtime lifecycle.
2. A lightweight Neo4j test instance for registering and testing procedures/functions before server deployment.
3. Usually no; external side effects should live in application/workflow services with idempotent coordination.
4. No. It is removed in Cypher 25; current trigger installation uses the newer procedures.
5. The tested server/plugin/configuration combination, not just one source file.
Summary and next step
Custom extensions are justified when a stable server-side capability clearly beats native Cypher/APOC/application alternatives and the team accepts the deployment burden. Lesson 5 turns that into a decision framework.
Authoritative references
- Current Neo4j versions — Release/LTS snapshot used for this chapter.
- APOC Core 2026.07 documentation — Current officially supported APOC Core manual.
- APOC installation and compatibility — Matching Neo4j/APOC year-month lines, deployment and restart requirements.
- APOC introduction: Core vs Extended — Support boundary between officially supported Core and community-maintained Extended.
- APOC security guidelines — Allowlist/unrestricted execution, file/network and SSRF guidance.
- APOC configuration — apoc.conf location, file/HTTP/trigger-related settings and defaults.
- APOC procedures and functions — Current procedure/function catalog and Cypher 25 status.
- Neo4j Java Reference: extending Neo4j — Custom procedures, functions and server extension boundaries.
- Custom plugin project setup — Current Maven, Java 21 and Neo4j 2026.07.1 project example.
- Unmanaged server extensions — Advanced HTTP extension boundary and resource warning.