spring-data-neo4j

$npx mdskill add giuseppe-trisciuoglio/developer-kit/spring-data-neo4j

Integrate Neo4j graph database with Spring Boot applications.

  • Maps Java objects to Neo4j nodes and relationships.
  • Uses Spring Boot starter and Neo4j driver for connectivity.
  • Analyzes entity annotations and repository patterns to generate code.
  • Produces annotated entity classes, repositories, and test configurations.

SKILL.md

.github/skills/spring-data-neo4jView on GitHub ↗
---
name: spring-data-neo4j
description: Provides Spring Data Neo4j integration patterns for Spring Boot applications. Use when you need to work with a graph database, Neo4j nodes and relationships, Cypher queries, or Spring Data Neo4j. Creates node entities with @Node annotation, defines relationships with @Relationship, writes Cypher queries using @Query, configures imperative and reactive Neo4j repositories, implements graph traversal patterns, and sets up testing with embedded databases.
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
---

# Spring Data Neo4j Integration Patterns

## Overview

Provides Spring Data Neo4j integration patterns for Spring Boot applications. Covers node entity mapping with `@Node` and `@Relationship`, repository configuration (imperative and reactive), custom Cypher queries with `@Query`, and integration testing with embedded Neo4j databases.

## When to Use

Use this skill when working with:
- Graph databases and Neo4j integration in Spring Boot
- Node entities, relationships, and Cypher queries
- Spring Data Neo4j repositories (imperative or reactive)
- Neo4j testing with embedded databases

## Instructions

### 1. Set Up Spring Data Neo4j

**Add the dependency:**

Maven:
```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
```

Gradle:
```groovy
implementation 'org.springframework.boot:spring-boot-starter-data-neo4j'
```

**Configure connection in application.properties:**
```properties
spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret
```

**Configure Cypher-DSL dialect (recommended):**
```java
@Configuration
public class Neo4jConfig {
    @Bean
    Configuration cypherDslConfiguration() {
        return Configuration.newConfig()
            .withDialect(Dialect.NEO4J_5).build();
    }
}
```

> **Validation Checkpoint**: Run `MATCH (n) RETURN count(n)` via cypher-shell to verify the connection works before proceeding.

### 2. Define Node Entities

1. **Use `@`Node annotation** to mark entity classes
2. **Choose ID strategy:**
   - Business key as `@`Id (immutable, natural identifier)
   - Generated `@`Id `@`GeneratedValue (Neo4j internal ID)
3. **Define relationships** with `@`Relationship annotation
4. **Keep entities immutable** with final fields
5. **Use `@`Property** for custom property names

> **Validation Checkpoint**: If entity save fails, check for constraint violations—duplicate IDs violate uniqueness constraints.

### 3. Create Repositories

1. **Extend repository interface:**
   - `Neo4jRepository<Entity, ID>` for imperative operations
   - `ReactiveNeo4jRepository<Entity, ID>` for reactive operations
2. **Use query derivation** for simple queries
3. **Apply `@`Query annotation** for complex Cypher queries
4. **Use `$`paramName syntax** for parameters

> **Validation Checkpoint**: Test repository with `findAll()` first—if empty, verify the Neo4j instance is running and credentials are correct.

### 4. Test Your Implementation

1. **Use `@`DataNeo4jTest** for repository testing with test slicing
2. **Set up Neo4j Harness** with embedded database and fixtures
3. **Provide test data** via `withFixture()` Cypher queries
4. **Clean up test data** between tests

> **Validation Checkpoint**: If tests fail with "Connection refused", ensure the embedded Neo4j started successfully in `@BeforeAll`.

## Basic Entity Mapping

### Node Entity with Business Key

```java
@Node("Movie")
public class MovieEntity {

    @Id
    private final String title;  // Business key as ID

    @Property("tagline")
    private final String description;

    private final Integer year;

    @Relationship(type = "ACTED_IN", direction = Direction.INCOMING)
    private List<Roles> actorsAndRoles = new ArrayList<>();

    @Relationship(type = "DIRECTED", direction = Direction.INCOMING)
    private List<PersonEntity> directors = new ArrayList<>();

    public MovieEntity(String title, String description, Integer year) {
        this.title = title;
        this.description = description;
        this.year = year;
    }
}
```

### Node Entity with Generated ID

```java
@Node("Movie")
public class MovieEntity {

    @Id @GeneratedValue
    private Long id;

    private final String title;

    @Property("tagline")
    private final String description;

    public MovieEntity(String title, String description) {
        this.id = null;  // Never set manually
        this.title = title;
        this.description = description;
    }

    // Wither method for immutability with generated IDs
    public MovieEntity withId(Long id) {
        if (this.id != null && this.id.equals(id)) {
            return this;
        } else {
            MovieEntity newObject = new MovieEntity(this.title, this.description);
            newObject.id = id;
            return newObject;
        }
    }
}
```

## Repository Patterns

### Basic Repository Interface

```java
@Repository
public interface MovieRepository extends Neo4jRepository<MovieEntity, String> {

    // Query derivation from method name
    MovieEntity findOneByTitle(String title);

    List<MovieEntity> findAllByYear(Integer year);

    List<MovieEntity> findByYearBetween(Integer startYear, Integer endYear);
}
```

### Reactive Repository

```java
@Repository
public interface MovieRepository extends ReactiveNeo4jRepository<MovieEntity, String> {

    Mono<MovieEntity> findOneByTitle(String title);

    Flux<MovieEntity> findAllByYear(Integer year);
}
```

**Imperative vs Reactive:**
- Use `Neo4jRepository` for blocking, imperative operations
- Use `ReactiveNeo4jRepository` for non-blocking, reactive operations
- **Do not mix imperative and reactive in the same application**
- Reactive requires Neo4j 4+ on the database side

## Custom Queries with `@`Query

```java
@Repository
public interface AuthorRepository extends Neo4jRepository<Author, Long> {

    @Query("MATCH (b:Book)-[:WRITTEN_BY]->(a:Author) " +
           "WHERE a.name = $name AND b.year > $year " +
           "RETURN b")
    List<Book> findBooksAfterYear(@Param("name") String name,
                                   @Param("year") Integer year);

    @Query("MATCH (b:Book)-[:WRITTEN_BY]->(a:Author) " +
           "WHERE a.name = $name " +
           "RETURN b ORDER BY b.year DESC")
    List<Book> findBooksByAuthorOrderByYearDesc(@Param("name") String name);
}
```

**Custom Query Best Practices:**
- Use `$parameterName` for parameter placeholders
- Use `@Param` annotation when parameter name differs from method parameter
- MATCH specifies node patterns and relationships
- WHERE filters results
- RETURN defines what to return

## Testing Strategies

### Neo4j Harness for Integration Testing

**Test Configuration:**
```java
@DataNeo4jTest
class BookRepositoryIntegrationTest {

    private static Neo4j embeddedServer;

    @BeforeAll
    static void initializeNeo4j() {
        embeddedServer = Neo4jBuilders.newInProcessBuilder()
            .withDisabledServer()  // No HTTP access needed
            .withFixture(
                "CREATE (b:Book {isbn: '978-0547928210', " +
                "name: 'The Fellowship of the Ring', year: 1954})" +
                "-[:WRITTEN_BY]->(a:Author {id: 1, name: 'J. R. R. Tolkien'}) " +
                "CREATE (b2:Book {isbn: '978-0547928203', " +
                "name: 'The Two Towers', year: 1956})" +
                "-[:WRITTEN_BY]->(a)"
            )
            .build();
    }

    @AfterAll
    static void stopNeo4j() {
        embeddedServer.close();
    }

    @DynamicPropertySource
    static void neo4jProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.neo4j.uri", embeddedServer::boltURI);
        registry.add("spring.neo4j.authentication.username", () -> "neo4j");
        registry.add("spring.neo4j.authentication.password", () -> "null");
    }

    @Autowired
    private BookRepository bookRepository;

    @Test
    void givenBookExists_whenFindOneByTitle_thenBookIsReturned() {
        Book book = bookRepository.findOneByTitle("The Fellowship of the Ring");
        assertThat(book.getIsbn()).isEqualTo("978-0547928210");
    }
}
```

## Examples

### Example 1: Saving and Retrieving Entities

**Input:**
```java
MovieEntity movie = new MovieEntity("The Matrix", "Welcome to the Real World", 1999);
movieRepository.save(movie);

MovieEntity found = movieRepository.findOneByTitle("The Matrix");
```

**Output:**
```java
MovieEntity{
    title="The Matrix",
    description="Welcome to the Real World",
    year=1999,
    actorsAndRoles=[],
    directors=[]
}
```

### Example 2: Custom Cypher Query

**Input:**
```java
List<Book> books = authorRepository.findBooksAfterYear("J.R.R. Tolkien", 1950);
```

**Output:**
```java
[
    Book{isbn="978-0547928210", name="The Fellowship of the Ring", year=1954},
    Book{isbn="978-0547928203", name="The Two Towers", year=1956},
    Book{isbn="978-0547928227", name="The Return of the King", year=1957}
]
```

### Example 3: Relationship Traversal

**Input:**
```java
@Query("MATCH (m:Movie)<-[:ACTED_IN]-(a:Person) " +
       "WHERE m.title = $title RETURN a.name as actorName")
List<String> findActorsByMovieTitle(@Param("title") String title);

List<String> actors = movieRepository.findActorsByMovieTitle("The Matrix");
```

**Output:**
```java
["Keanu Reeves", "Laurence Fishburne", "Carrie-Anne Moss", "Hugo Weaving"]
```

---

Progress from basic to advanced examples covering complete movie database, social network patterns, e-commerce product catalogs, custom queries, and reactive operations.

See [examples](./references/examples.md) for comprehensive code examples.

## Best Practices

### Entity Design
- Use immutable entities with final fields
- Choose between business keys (`@`Id) or generated IDs (`@`Id `@`GeneratedValue)
- Keep entities focused on graph structure, not business logic
- Use proper relationship directions (INCOMING, OUTGOING, UNDIRECTED)

### Repository Design
- Extend `Neo4jRepository` for imperative or `ReactiveNeo4jRepository` for reactive
- Use query derivation for simple queries
- Write custom `@`Query for complex graph patterns
- Don't mix imperative and reactive in same application

### Configuration
- Always configure Cypher-DSL dialect explicitly
- Use environment-specific properties for credentials
- Never hardcode credentials in source code
- Configure connection pooling based on load

### Testing
- Use Neo4j Harness for integration tests
- Provide test data via `withFixture()` Cypher queries
- Use `@DataNeo4jTest` for test slicing
- Test both successful and edge-case scenarios

### Architecture
- Use constructor injection exclusively
- Separate domain entities from DTOs
- Follow feature-based package structure
- Keep domain layer framework-agnostic

### Security
- Use Spring Boot property overrides for credentials
- Configure proper authentication and authorization
- Validate input parameters in service layer
- Use parameterized queries to prevent Cypher injection

## Constraints and Warnings

- Do not mix imperative and reactive repositories in the same application.
- Neo4j transactions are required for write operations; ensure `@Transactional` is properly configured.
- Be cautious with deep relationship traversal as it can cause performance issues.
- Large result sets should be paginated to avoid memory problems.
- Cypher queries are case-sensitive; ensure consistent casing in property names.
- Immutable entities require proper wither methods for generated IDs.
- Relationships in Spring Data Neo4j are not lazy-loaded by default; consider projection for large graphs.
- The Neo4j Java driver is not compatible with reactive streams; use the reactive driver for reactive operations.

## Troubleshooting

| Problem | Cause | Solution |
|---------|-------|----------|
| `Connection refused` on localhost:7687 | Neo4j server not running | Start Neo4j or use embedded Neo4j for tests |
| `Authentication failed` | Wrong credentials | Check `spring.neo4j.authentication.username/password` |
| Entity not saved / `MATCH` returns nothing | Transaction not committed | Add `@Transactional` or verify auto-commit settings |
| `ConstraintViolationException` on save | Duplicate `@Id` value | Ensure IDs are unique or use `@GeneratedValue` |
| Relationships missing in results | Wrong `@Relationship` direction | Check `Direction.INCOMING/OUTGOING/UNDIRECTED` |
| `@Query` returns wrong data | Cypher parameter syntax | Use `$paramName` not `$ {paramName}` |
| Test fails with `@DataNeo4jTest` | Embedded Neo4j not started | Ensure `@BeforeAll` starts Neo4j before tests |

## References

For detailed documentation including complete API reference, Cypher query patterns, and configuration options:

- [Annotations Reference](./references/reference.md#annotations-reference)
- [Cypher Query Language](./references/reference.md#cypher-query-language)
- [Configuration Properties](./references/reference.md#configuration-properties)
- [Repository Methods](./references/reference.md#repository-methods)
- [Projections and DTOs](./references/reference.md#projections-and-dtos)
- [Transaction Management](./references/reference.md#transaction-management)
- [Performance Tuning](./references/reference.md#performance-tuning)

### External Resources
- [Spring Data Neo4j Official Documentation](https://docs.spring.io/spring-data/neo4j/reference/)
- [Neo4j Developer Guide](https://neo4j.com/developer/)
- [Spring Data Commons Documentation](https://docs.spring.io/spring-data/commons/reference/)

More from giuseppe-trisciuoglio/developer-kit

SkillDescription
adr-draftingCreates new Architecture Decision Record (ADR) documents for significant architectural changes using a consistent template and repository-aware naming and storage guidance. Use when a user or agent decides on an architectural change, needs to document technical rationale, or wants to add a new ADR to the project history.
aws-cdkProvides AWS CDK TypeScript patterns for defining, validating, and deploying AWS infrastructure as code. Use when creating CDK apps, stacks, and reusable constructs, modeling serverless or VPC-based architectures, applying IAM and encryption defaults, or testing and reviewing `cdk synth`, `cdk diff`, and `cdk deploy` changes. Triggers include "aws cdk typescript", "create cdk app", "cdk stack", "cdk construct", "cdk deploy", and "cdk test".
aws-cli-beastProvides advanced AWS CLI patterns for managing EC2, Lambda, S3, DynamoDB, RDS, VPC, IAM, and CloudWatch. Generates bulk operation scripts, automates cross-service workflows, validates security configurations, and executes JMESPath queries for complex filtering. Triggers on "aws cli help", "aws command line", "aws scripting", "aws automation", "aws batch operations", "aws bulk operations", "aws cli pagination", "aws multi-region", "aws profiles", "aws cli troubleshooting".
aws-cloudformation-auto-scalingProvides AWS CloudFormation patterns for Auto Scaling including EC2, ECS, and Lambda. Use when creating Auto Scaling groups, launch configurations, launch templates, scaling policies, lifecycle hooks, and predictive scaling. Covers template structure with Parameters, Outputs, Mappings, Conditions, cross-stack references, and best practices for high availability and cost optimization.
aws-cloudformation-bedrockProvides AWS CloudFormation patterns for Amazon Bedrock resources including agents, knowledge bases, data sources, guardrails, prompts, flows, and inference profiles. Use when creating Bedrock agents with action groups, implementing RAG with knowledge bases, configuring vector stores, setting up content moderation guardrails, managing prompts, orchestrating workflows with flows, and configuring inference profiles for model optimization.
aws-cloudformation-cloudfrontProvides AWS CloudFormation patterns for CloudFront distributions, origins (ALB, S3, Lambda@Edge, VPC Origins), CacheBehaviors, Functions, SecurityHeaders, parameters, Outputs and cross-stack references. Use when creating CloudFront distributions with CloudFormation, configuring multiple origins, implementing caching strategies, managing custom domains with ACM, configuring WAF, and optimizing performance.
aws-cloudformation-cloudwatchProvides AWS CloudFormation patterns for CloudWatch monitoring, metrics, alarms, dashboards, logs, and observability. Use when creating CloudWatch metrics, alarms, dashboards, log groups, log subscriptions, anomaly detection, synthesized canaries, Application Signals, and implementing template structure with Parameters, Outputs, Mappings, Conditions, cross-stack references, and CloudWatch best practices for monitoring production infrastructure.
aws-cloudformation-dynamodbProvides AWS CloudFormation patterns for DynamoDB tables, GSIs, LSIs, auto-scaling, and streams. Use when creating DynamoDB tables with CloudFormation, configuring primary keys, local/global secondary indexes, capacity modes (on-demand/provisioned), point-in-time recovery, encryption, TTL, and implementing template structure with Parameters, Outputs, Mappings, Conditions, cross-stack references.
aws-cloudformation-ec2Provides AWS CloudFormation patterns for EC2 instances, Security Groups, IAM roles, and load balancers. Use when creating EC2 instances, SPOT instances, Security Groups, IAM roles for EC2, Application Load Balancers (ALB), Target Groups, and implementing template structure with Parameters, Outputs, Mappings, Conditions, and cross-stack references.
aws-cloudformation-ecsProvides AWS CloudFormation patterns for ECS clusters, task definitions, services, container definitions, auto scaling, blue/green deployments, CodeDeploy integration, ALB integration, service discovery, monitoring, logging, template structure, parameters, outputs, and cross-stack references. Use when creating ECS clusters with CloudFormation, configuring Fargate and EC2 launch types, implementing blue/green deployments, managing auto scaling, integrating with ALB and NLB, and implementing ECS best practices.