Graph Databases: Neo4j
Relevant source files
The following files were used as context for generating this wiki page:
Forze provides integration with Neo4j through a specialized adapter that implements the framework's graph contracts. This integration allows applications to model complex relationships using vertices (nodes) and edges, supporting operations such as neighborhood expansion, shortest-path queries, and multi-tenant isolation. Unlike document-based integrations, the Neo4j adapter focuses on graph-native traversals and Cypher-based execution.
The implementation is partitioned into a kernel for low-level client operations and an adapter layer that maps logical GraphModuleSpec definitions to physical Neo4j structures. It supports both keyed edges and endpoint-based relationship identity.
Sources: src/forze_neo4j/adapters/graph.py:1-15, skills/forze-graph-contracts/SKILL.md:1-10
Architecture and Components
The Neo4j integration follows the Hexagonal Architecture pattern used throughout Forze. The Neo4jGraphAdapter acts as the primary implementation of the GraphQueryPort, GraphCommandPort, and GraphRawQueryPort protocols.
Port Implementation
The adapter translates Forze's high-level graph commands into Cypher queries executed via the Neo4jClientPort.
The diagram above shows the relationship between Forze's core graph protocols and the Neo4j-specific adapter implementation.
Sources: src/forze_neo4j/adapters/graph.py:53-120, tests/unit/test_forze/application/contracts/test_graph.py:441-525
Multi-Tenancy and Isolation
Tenancy in Neo4j is enforced using a property-based partition. A specific property (defaulting to tenant_id) is stamped on nodes during writes and used as a predicate in read queries.
Traversal Isolation Modes
The adapter supports two isolation strategies for traversals:
full-path(Default): Constrains every node in a traversal result (neighbors, expansion, or shortest path) to the tenant. This prevents cross-tenant edges from surfacing foreign data.anchor: Only constrains the start or endpoint nodes. This is more performant but assumes no edges ever cross tenant boundaries.
Sources: src/forze_neo4j/adapters/graph.py:73-82, tests/integration/test_forze_neo4j/test_neo4j_graph.py:149-215
Tenant Scoping Flow
The sequence shows how the adapter injects tenant constraints into Cypher queries to ensure data isolation.
Sources: src/forze_neo4j/adapters/graph.py:230-245, tests/unit/test_forze_neo4j/test_adapter_graph.py:157-200
Graph Specifications and Mapping
The system relies on GraphModuleSpec to define the logical schema. This specification maps node kinds and edge kinds to Pydantic models for serialization.
Configuration Table
| Component | Type | Description |
|---|---|---|
GraphNodeSpec | Class | Defines a vertex type, including its name, read model, and optional create/update models. |
GraphEdgeSpec | Class | Defines a relationship, its directionality (DIRECTED or SYMMETRIC), and endpoints. |
tenant_property | Config | The Neo4j property name used for tenant isolation (default: tenant_id). |
allow_raw_query | Config | Boolean flag to enable/disable the Cypher escape hatch. Disabled by default for security. |
database | Spec | Target database; can be a static name or a per-tenant resolver. |
Sources: src/forze_neo4j/adapters/graph.py:58-71, tests/unit/test_forze/application/contracts/test_graph.py:86-130
Key Classes and Data Structures
VertexRef: A frozen reference to a vertex containingkindandkey.EdgeRef: A reference to an edge, which can be identified by a businesskeyor by itsfrom_refandto_refendpoints.NeighborRow: A result structure for neighborhood queries containing theothervertex and thevia_edge.GraphWalkStep: Represents a single step in a graph expansion, including the current vertex, its depth, and parent reference.
Sources: src/forze_neo4j/adapters/graph.py:26-44, tests/unit/test_forze/application/contracts/test_graph.py:202-230
Data Operations
Vertex and Edge CRUD
The adapter provides standard operations for creating, updating, and deleting graph elements. Edge creation requires identifying endpoints via from_key and to_key within the command payload.
# Example vertex creation via adapter
created = await adapter.create_vertex(
"User",
UserCreate(id="u1", name="Alice")
)
# Example edge creation between two keys
await adapter.create_edge(
"FOLLOWS",
FollowsCreate(from_key="u1", to_key="u2", weight=10)
)
Sources: src/forze_neo4j/adapters/graph.py:348-435, tests/integration/test_forze_neo4j/test_neo4j_graph.py:84-110
Graph Traversals
Traversals are implemented using native Cypher builders for performance.
neighbors: Retrieves immediate neighbors connected by specific edge types.expand: Performs a variable-depth search up to amax_depth.shortest_path: Finds the path between two vertices with the minimum number of hops.scoped_walk: A structured walk that allows multi-hop segments toward a specifictarget_kind.
Sources: src/forze_neo4j/adapters/graph.py:220-346, tests/integration/test_forze_neo4j/test_neo4j_graph.py:112-147
Raw Query Escape Hatch
For complex queries not covered by the structured ports, the GraphRawQueryPort provides a run method. This allows raw Cypher execution while still enforcing tenant isolation if tenant_aware is enabled.
Security Note: The raw hatch is disabled by default (allow_raw_query=False). When enabled, the adapter automatically injects a $tenant parameter into the query execution context if a tenant is bound.
Sources: src/forze_neo4j/adapters/graph.py:441-468, tests/unit/test_forze_neo4j/test_adapter_graph.py:240-270
Conclusion
The Neo4j integration provides a robust, tenant-aware graph implementation for Forze. By separating logical graph specifications from Cypher execution, it maintains architectural purity while leveraging Neo4j's native traversal capabilities. Developers can use structured ports for common graph patterns or the raw escape hatch for advanced analytics, all while benefiting from the framework's multi-tenancy guarantees.