AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Neo4j Spring Data Skill

skill-neo4j-contrib-neo4j-skills-neo4j-spring-data-skill · by neo4j-contrib

Use when building Spring Boot applications with Neo4j using Spring Data Neo4j (SDN 7.x/8.x):

No reviews yet
0 installs
38 views
0.0% view→install

Install

$ agentstack add skill-neo4j-contrib-neo4j-skills-neo4j-spring-data-skill

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-neo4j-contrib-neo4j-skills-neo4j-spring-data-skill)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Neo4j Spring Data Skill? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Neo4j Spring Data Skill

When to Use

  • Configuring Spring Boot with Neo4j (spring-boot-starter-data-neo4j)
  • Writing @Node entity classes and @Relationship/@RelationshipProperties mappings
  • Defining Neo4jRepository or ReactiveNeo4jRepository interfaces
  • Writing @Query annotations with Cypher on repository methods
  • Using Spring projections (interface-based, DTO, dynamic) with Neo4j
  • Configuring application.yml for Neo4j connection
  • Custom queries via Neo4jClient or Neo4jTemplate
  • Spring AI Neo4jVectorStore for vector search in Spring apps
  • Transaction management, auditing, optimistic locking

When NOT to Use

  • Raw Java driver without Springneo4j-driver-java-skill
  • Cypher query authoringneo4j-cypher-skill
  • Driver version upgradesneo4j-migration-skill
  • GDS algorithmsneo4j-gds-skill

Version Matrix

| SDN | Spring Boot | Spring Framework | Java | Neo4j | |--------|-------------|-----------------|------|-------| | 8.0.x | 3.3.x / 3.4.x | 6.2.x | 17+ | 5.15+ | | 8.1.x | 3.4.x+ | 7.0.x | 17+ | 5.15+ | | 7.5.x | 3.2.x | 6.1.x | 17+ | 4.4+ |

Use spring-boot-starter-data-neo4j — it pulls SDN + driver. No explicit SDN version needed when using Spring Boot BOM.


Setup

Maven


    org.springframework.boot
    spring-boot-starter-data-neo4j

Gradle

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

Reactive stack (add alongside above)


    org.springframework.boot
    spring-boot-starter-webflux

Configuration

application.yml — imperative (standard)

spring:
  neo4j:
    uri: ${NEO4J_URI:bolt://localhost:7687}
    authentication:
      username: ${NEO4J_USERNAME:neo4j}
      password: ${NEO4J_PASSWORD}
  data:
    neo4j:
      database: ${NEO4J_DATABASE:neo4j}

application.yml — Aura (TLS required)

spring:
  neo4j:
    uri: ${NEO4J_URI}            # neo4j+s://xxxx.databases.neo4j.io
    authentication:
      username: ${NEO4J_USERNAME:neo4j}
      password: ${NEO4J_PASSWORD}
  data:
    neo4j:
      database: ${NEO4J_DATABASE:neo4j}

Credentials: store in .env; never hardcode. Verify .env is in .gitignore.


Entity Mapping

import org.springframework.data.neo4j.core.schema.*;

// Internal generated ID (default for most cases)
@Node("Person")
public class PersonEntity {
    @Id @GeneratedValue private Long id;         // element ID (Long)
    private String name;
    @Property("birth_year") private Integer birthYear;  // custom property name
    @Relationship(type = "KNOWS", direction = Relationship.Direction.OUTGOING)
    private List friends = new ArrayList<>();
}

// UUID business key
@Node("Product")
public class ProductEntity {
    @Id @GeneratedValue(generatorClass = GeneratedValue.UUIDStringGenerator.class)
    private String id;
    @Version private Long version;               // optimistic locking; required with business key
}

// User-assigned key (caller sets value; no @GeneratedValue)
@Node("Country")
public class CountryEntity {
    @Id private String isoCode;
    private String name;
}

// Multiple static labels
@Node(primaryLabel = "Vehicle", labels = {"Car", "Auditable"})
public class CarEntity { ... }

// Runtime labels
@Node("Content")
public class ContentEntity {
    @Id @GeneratedValue private Long id;
    @DynamicLabels private Set tags = new HashSet<>();  // labels added at runtime
}

Relationship Properties

Use @RelationshipProperties when the relationship itself carries data.

@RelationshipProperties
public class RolesRelationship {

    @RelationshipId                     // internal relationship ID; required
    private Long id;

    private List roles;

    @TargetNode                         // marks the other end of the relationship
    private PersonEntity person;
}
@Node("Movie")
public class MovieEntity {

    @Id @GeneratedValue
    private Long id;

    private String title;

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

Repository Interfaces

Basic CRUD

import org.springframework.data.neo4j.repository.Neo4jRepository;

public interface PersonRepository extends Neo4jRepository {

    Optional findByName(String name);

    List findByBirthYearBetween(int from, int to);

    List findByNameContainingIgnoreCase(String fragment);

    long countByBirthYearGreaterThan(int year);

    void deleteByName(String name);
}

@Query — custom Cypher

// CORRECT: $param bound parameter
@Query("MATCH (p:Person {name: $name})-[:KNOWS]->(f:Person) RETURN f")
List findFriendsOf(String name);

// With pagination
@Query(value = "MATCH (p:Person) RETURN p ORDER BY p.name",
       countQuery = "MATCH (p:Person) RETURN count(p)")
Page findAllPaged(Pageable pageable);

// Return relationship-rich entity; map target via @Node return
@Query("MATCH (m:Movie) findMoviesActedInBy(String name);

Security rule: NEVER string-concatenate user input into Cypher. Always use $paramName.

Pagination and sorting

Page findByBirthYearGreaterThan(int year, Pageable pageable);

List findTop10ByOrderByNameAsc();

List findByName(String name, Sort sort);

Usage:

Pageable page = PageRequest.of(0, 20, Sort.by("name").ascending());
Page result = repo.findByBirthYearGreaterThan(1980, page);

Projections

Interface projection (closed — query-optimizable)

public interface PersonSummary {
    String getName();
    Integer getBirthYear();
}

List findByBirthYearLessThan(int year);

DTO projection (record — preferred in Java 17+)

public record PersonDto(String name, Integer birthYear) {}

List findByName(String name);

Dynamic projection

 List findByName(String name, Class type);

// Usage
repo.findByName("Alice", PersonSummary.class);
repo.findByName("Alice", PersonEntity.class);

Open projection — SpEL (disables query optimization)

public interface FullName {
    @Value("#{target.name + ' (' + target.birthYear + ')'}") String getDisplayName();
}

Reactive Repository

import org.springframework.data.neo4j.repository.ReactiveNeo4jRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public interface ReactivePersonRepository extends ReactiveNeo4jRepository {

    Mono findByName(String name);

    @Query("MATCH (p:Person {name: $name})-[:KNOWS]->(f) RETURN f")
    Flux findFriendsOf(String name);
}

Do NOT mix imperative and reactive database access in the same application context.


Custom Repository Implementation

Fragment pattern — use when @Query is not enough.

// 1. Fragment interface
public interface PersonRepositoryCustom {
    List findByComplexCriteria(String criteria);
}

// 2. Impl — must end with "Impl"
public class PersonRepositoryCustomImpl implements PersonRepositoryCustom {
    private final Neo4jClient neo4jClient;
    PersonRepositoryCustomImpl(Neo4jClient c) { this.neo4jClient = c; }

    @Override
    public List findByComplexCriteria(String c) {
        return new ArrayList<>(neo4jClient
            .query("MATCH (p:Person) WHERE p.name CONTAINS $c RETURN p").bind(c).to("c")
            .fetchAs(PersonEntity.class)
            .mappedBy((t, r) -> { var e = new PersonEntity(); e.setName(r.get("p").asNode().get("name").asString()); return e; })
            .all());
    }
}

// 3. Compose
public interface PersonRepository extends Neo4jRepository, PersonRepositoryCustom {}

Neo4jClient — Low-Level Queries

Use when @Query is insufficient or you need full control over Cypher execution.

// Bind params + fetch single scalar
neo4jClient.query("MATCH (p:Person {name: $name}) RETURN count(*) AS cnt")
    .bind("Alice").to("name")
    .fetchAs(Long.class)
    .mappedBy((t, r) -> r.get("cnt").asLong())
    .one();

// Bind + run write (no result)
neo4jClient.query("MERGE (p:Person {name: $name})")
    .bind(personName).to("name")
    .run();

// Custom object mapping
neo4jClient.query("MATCH (p:Person)-[:DIRECTED]->(m:Movie) WHERE p.name=$n RETURN p, collect(m) AS movies")
    .bind("Lilly Wachowski").to("n")
    .fetchAs(Director.class)
    .mappedBy((typeSystem, record) -> new Director(
        record.get("p").asNode().get("name").asString(),
        record.get("movies").asList(v -> new Movie(v.get("title").asString()))
    )).one();

Full API: [references/neo4j-client.md](references/neo4j-client.md)


Transaction Management

@Service
@Transactional                      // class-level: all methods transactional
public class PersonService {
    @Transactional(readOnly = true) // read-only hint
    public Optional findByName(String name) { ... }

    @Transactional                  // explicit write
    public PersonEntity save(PersonEntity p) { return repository.save(p); }
}

Neo4jTransactionManager auto-configured. Do NOT mix with JPA PlatformTransactionManager without explicit qualifier. Use @Transactional on concrete class, not interface.


Spring AI — Neo4jVectorStore

Dependency


    org.springframework.ai
    spring-ai-starter-vector-store-neo4j

application.yml

spring:
  ai:
    vectorstore:
      neo4j:
        initialize-schema: true         # creates vector index on first run
        index-name: my-index
        embedding-dimension: 1536       # must match your embedding model
        distance-type: cosine           # cosine (default) or euclidean
        label: Document                 # node label for stored chunks
        embedding-property: embedding   # property for the vector

Requires Neo4j 5.15+. Reuses spring.neo4j.* connection config.

Usage

@Autowired VectorStore vectorStore;

// Store
vectorStore.add(List.of(new Document("text", Map.of("author", "alice"))));

// Similarity search
List results = vectorStore.similaritySearch(
    SearchRequest.builder().query("spring neo4j").topK(5).similarityThreshold(0.75).build()
);

// With metadata filter
vectorStore.similaritySearch(
    SearchRequest.builder().query("spring neo4j").topK(5)
        .filterExpression("author == 'alice'").build()
);

Common Errors

| Error | Cause | Fix | |---|---|---| | MappingException: Could not find entity | Entity not scanned | Check @EnableNeo4jRepositories base package | | Relationships null after load | Default depth may skip deep rels | Use @Query with RETURN m, collect(r), collect(p) | | N+1 queries | Per-entity relationship fetch | Rewrite with single @Query; use projections | | OptimisticLockingFailureException | Stale @Version on concurrent write | Retry in service layer | | IllegalStateException: Cannot mix reactive/imperative | Both repo types in same context | Pick one stack | | Projection null fields | Getter name mismatch | Match getter to property name; check @Property alias | | @Query empty with rels | Missing collect(r), collect(p) | Return root node + rels + related nodes together | | Cannot delete node, node has relationships | deleteById without detach | Use @Query with DETACH DELETE | | Transaction not rolling back | @Transactional on interface | Apply on concrete service class |


Relationship Loading — Key Rule

SDN loads related entities eagerly up to a configured depth (default: 1 hop). For deeper graphs:

// Explicit @Query to control what gets loaded
@Query("""
    MATCH (m:Movie) findByTitleWithCast(String title);

collect(r), collect(p) in RETURN is required for SDN to map @RelationshipProperties correctly.


References


Checklist

  • [ ] @Node uses explicit label string, not default class name
  • [ ] @Id @GeneratedValue (or @Id + @Version for business key with optimistic lock)
  • [ ] @RelationshipProperties class has @RelationshipId and @TargetNode
  • [ ] @Relationship direction is explicit (OUTGOING / INCOMING)
  • [ ] @Query Cypher uses $paramName — no string concatenation
  • [ ] Relationship-rich @Query returns collect(r), collect(p) alongside root node
  • [ ] Database name set in application.yml (avoids default DB ambiguity)
  • [ ] Unique constraint exists in DB for any business key used in repository lookups
  • [ ] @Transactional on concrete service class (not interface)
  • [ ] No imperative + reactive mix in same application context
  • [ ] Credentials in env vars; .env in .gitignore
  • [ ] spring.ai.vectorstore.neo4j.initialize-schema: true for first run (Spring AI)

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.