Provides Spring Data Neo4j integration patterns for Spring Boot apps, including @Node entities, @Relationship, Cypher @Query, imperative/reactive repositories, graph traversals, and embedded testing.
From developer-kit-javanpx claudepluginhub giuseppe-trisciuoglio/developer-kit --plugin developer-kit-javaThis skill is limited to using the following tools:
references/examples.mdreferences/reference.mdSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
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.
Use this skill when working with:
Add the dependency:
Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
Gradle:
implementation 'org.springframework.boot:spring-boot-starter-data-neo4j'
Configure connection in application.properties:
spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret
Configure Cypher-DSL dialect (recommended):
@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.
@Node annotation to mark entity classes@Id (immutable, natural identifier)@Id @GeneratedValue (Neo4j internal ID)@Relationship annotation@Property for custom property namesValidation Checkpoint: If entity save fails, check for constraint violations—duplicate IDs violate uniqueness constraints.
Neo4jRepository<Entity, ID> for imperative operationsReactiveNeo4jRepository<Entity, ID> for reactive operations@Query annotation for complex Cypher queries$paramName syntax for parametersValidation Checkpoint: Test repository with
findAll()first—if empty, verify the Neo4j instance is running and credentials are correct.
@DataNeo4jTest for repository testing with test slicingwithFixture() Cypher queriesValidation Checkpoint: If tests fail with "Connection refused", ensure the embedded Neo4j started successfully in
@BeforeAll.
@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("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
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);
}
@Repository
public interface MovieRepository extends ReactiveNeo4jRepository<MovieEntity, String> {
Mono<MovieEntity> findOneByTitle(String title);
Flux<MovieEntity> findAllByYear(Integer year);
}
Imperative vs Reactive:
Neo4jRepository for blocking, imperative operationsReactiveNeo4jRepository for non-blocking, reactive operations@Query@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:
$parameterName for parameter placeholders@Param annotation when parameter name differs from method parameterTest Configuration:
@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");
}
}
Input:
MovieEntity movie = new MovieEntity("The Matrix", "Welcome to the Real World", 1999);
movieRepository.save(movie);
MovieEntity found = movieRepository.findOneByTitle("The Matrix");
Output:
MovieEntity{
title="The Matrix",
description="Welcome to the Real World",
year=1999,
actorsAndRoles=[],
directors=[]
}
Input:
List<Book> books = authorRepository.findBooksAfterYear("J.R.R. Tolkien", 1950);
Output:
[
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}
]
Input:
@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:
["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 for comprehensive code examples.
@Id) or generated IDs (@Id @GeneratedValue)Neo4jRepository for imperative or ReactiveNeo4jRepository for reactive@Query for complex graph patternswithFixture() Cypher queries@DataNeo4jTest for test slicing@Transactional is properly configured.| 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 |
For detailed documentation including complete API reference, Cypher query patterns, and configuration options: