Skip to content

Conversation

@Manuel-Antunes
Copy link

Neo4j Driver Integration

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe:

What is the current behavior?

MikroORM currently supports SQL databases (PostgreSQL, MySQL, MariaDB, SQLite, MSSQL) and MongoDB as a document database. However, there is no native support for graph databases, which are increasingly important for modeling complex relationships and interconnected data structures such as social networks, recommendation engines, knowledge graphs, and organizational hierarchies.

Issue/Discussion: #4674

What is the new behavior?

This PR introduces comprehensive Neo4j graph database support to MikroORM through a new @mikro-orm/neo4j driver package.

Key Features:

  1. Complete Graph Database Support

    • Native Neo4j Bolt protocol integration via official neo4j-driver
    • Full CRUD operations optimized for graph structures
    • Transaction support with commit/rollback capabilities
  2. Graph-Specific Decorators

    • @Node() - Marks entities as Neo4j nodes with optional multiple labels
    • @Rel() - Defines relationships with explicit type and direction (IN/OUT)
    • @RelationshipProperties() - Enables relationships with rich metadata
    • @Field() - Alias for @Property() providing graph-style syntax
  3. Advanced Query Builder

    • Fluent API for constructing Cypher queries programmatically
    • Wraps Neo4j's official @neo4j/cypher-builder library
    • Supports complex patterns, variable-length relationships, subqueries
    • Full integration with MikroORM's entity system
  4. Relationship Modeling

    • First-class support for directed and undirected relationships
    • Many-to-one, many-to-many, self-referencing relationships
    • Relationship entities with properties (e.g., roles, timestamps, metadata)
    • Automatic relationship type inference from decorators
  5. Virtual Entities

    • Map Cypher query results to entity classes without persistence
    • Support for aggregations and computed properties
    • Dynamic virtual entities with parameterized queries
  6. Raw Cypher Execution

    • em.run() method for executing raw Cypher queries
    • em.aggregate() for aggregation queries
    • Full parameter binding and type conversion

Usage Examples:

Basic Node Definition:

import { Entity, PrimaryKey, Node, Field } from '@mikro-orm/neo4j';

@Entity()
@Node()
class Person {
  @Field({ primary: true })
  id: string = crypto.randomUUID();

  @Field()
  name!: string;

  @Field()
  age!: number;
}

Multiple Labels:

@Entity()
@Node({ labels: ['Employee', 'Manager'] })
class Executive {
  @Field({ primary: true })
  id: string = crypto.randomUUID();

  @Field()
  name!: string;

  @Field()
  department!: string;
}
// Creates nodes: (:Executive:Employee:Manager)

Relationships:

@Entity()
class Product {
  @PrimaryKey()
  id: string = crypto.randomUUID();

  @Property()
  name!: string;

  @ManyToOne(() => Category, { ref: true })
  @Rel({ type: 'PART_OF', direction: 'OUT' })
  category!: Ref<Category>;

  @ManyToMany(() => Tag)
  @Rel({ type: 'HAS_TAG', direction: 'OUT' })
  tags = new Collection<Tag>(this);
}

Relationship Properties:

@Entity()
@Node()
class Actor {
  @Field({ primary: true })
  id: string = crypto.randomUUID();

  @Field()
  name!: string;

  @OneToMany(() => ActedIn, a => a.actor)
  actedIn = new Collection<ActedIn>(this);
}

@Entity()
@RelationshipProperties({ type: 'ACTED_IN' })
class ActedIn {
  @PrimaryKey()
  id: string = crypto.randomUUID();

  @ManyToOne(() => Actor, { ref: true })
  actor!: Ref<Actor>;

  @ManyToOne(() => Movie, { ref: true })
  movie!: Ref<Movie>;

  @Property()
  roles!: string[];

  @Property()
  year!: number;
}

Query Builder:

const qb = em.createQueryBuilder<Movie>('Movie');

// Simple query
const movies = await qb
  .match()
  .where('released', 1999)
  .return(['title', 'released'])
  .execute();

// Advanced with relationships
const qb2 = em.createQueryBuilder<Movie>('Movie');
const node = qb2.getNode();
const Cypher = qb2.getCypher();

const result = await qb2
  .match()
  .related('ACTED_IN', 'left', 'Person')
  .where(Cypher.eq(node.property('title'), new Cypher.Param('The Matrix')))
  .orderBy('title', 'ASC')
  .limit(10)
  .execute();

Raw Cypher:

const result = await em.run<{ name: string; age: number }>(
  'MATCH (p:Person) WHERE p.age > $age RETURN p.name as name, p.age as age',
  { age: 25 }
);

Virtual Entities:

@Entity({
  expression: () => ({
    cypher: `
      MATCH (c:Category)
      RETURN {
        categoryName: c.categoryName,
        totalProducts: COUNT { (c)<-[:PART_OF]-(:Product) }
      } as node
    `,
  }),
})
class CategorySummary {
  @Property()
  categoryName!: string;

  @Property()
  totalProducts!: number;
}

const summaries = await em.find(CategorySummary, {});

Transactions:

await em.transactional(async em => {
  const person = em.create(Person, { name: 'Alice', age: 28 });
  const category = em.create(Category, { name: 'Electronics' });
  await em.flush();
});

Implementation Details:

New Package Structure:

packages/neo4j/
├── src/
│   ├── Neo4jConnection.ts          # Bolt protocol connection
│   ├── Neo4jDriver.ts               # Driver implementation
│   ├── Neo4jPlatform.ts             # Platform-specific behavior
│   ├── Neo4jEntityManager.ts        # Enhanced entity manager
│   ├── Neo4jEntityRepository.ts     # Graph-aware repository
│   ├── Neo4jQueryBuilder.ts         # Cypher query builder
│   ├── Neo4jCypherBuilder.ts        # Metadata utilities
│   ├── Neo4jSchemaGenerator.ts      # Schema operations
│   ├── Neo4jExceptionConverter.ts   # Error handling
│   ├── Neo4jMikroORM.ts             # Entry point
│   ├── decorators.ts                # Graph decorators
│   └── index.ts                     # Public API

Core Components:

  1. Neo4jDriver - Implements DatabaseDriver interface with graph-specific operations
  2. Neo4jConnection - Manages Neo4j sessions and transactions via Bolt protocol
  3. Neo4jEntityManager - Extends base EntityManager with run(), aggregate(), and createQueryBuilder()
  4. Neo4jQueryBuilder - Wraps @neo4j/cypher-builder with MikroORM entity awareness
  5. Neo4jCypherBuilder - Utility for extracting graph metadata from entity decorators
  6. Decorators - Graph-specific metadata storage separate from core MikroORM system

Metadata Storage:

  • Graph decorators use a separate WeakMap to avoid interfering with MikroORM's core metadata
  • @Node() stores additional labels on entity metadata
  • @Rel() stores relationship type and direction for properties
  • @RelationshipProperties() marks entities as relationship entities

Type Conversions:

  • Automatic conversion of Neo4j Integer objects to JavaScript numbers
  • Preservation of native JavaScript types through careful value handling
  • Support for complex nested objects and arrays

Does this PR introduce a breaking change?

  • Yes
  • No

This is a new feature that adds Neo4j support without affecting any existing functionality:

  • No changes to existing SQL or MongoDB drivers
  • No changes to core MikroORM APIs
  • Completely isolated in @mikro-orm/neo4j package
  • Opt-in usage - existing projects unaffected

Other information

Benefits:

  1. Native Graph Database Support

    • First-class support for graph data models
    • Optimized for highly connected data
    • Efficient traversal of complex relationships
  2. Familiar MikroORM API

    • Same find(), create(), flush(), remove() patterns
    • Transaction support via transactional() and begin()/commit()
    • Repository pattern with graph-specific extensions
  3. Flexible Querying

    • Type-safe query builder for complex Cypher queries
    • Raw Cypher execution for advanced use cases
    • Virtual entities for read-optimized data access
  4. Rich Relationship Modeling

    • Explicit relationship types and directions
    • Relationship properties for metadata
    • Self-referencing and circular relationships
  5. Production Ready

    • Official Neo4j driver integration
    • Comprehensive error handling
    • Transaction support with rollback
    • Connection pooling and session management

Testing:

Comprehensive Test Suite (tests/features/neo4j/Neo4jDriver.test.ts):

  • ✅ Basic CRUD operations (create, read, update, delete)
  • ✅ Many-to-one relationships with @Rel decorator
  • ✅ Many-to-many relationships (bidirectional)
  • ✅ Self-referencing relationships
  • ✅ Multiple labels via @Node({ labels: [...] })
  • ✅ Relationship properties via @RelationshipProperties
  • ✅ Population of relationships
  • ✅ Virtual entities (static and dynamic)
  • ✅ Query builder (match, create, merge, where, orderBy, limit, skip)
  • ✅ Query builder relationships (related, variable-length)
  • ✅ Query builder subqueries (call, exists, count)
  • ✅ Raw Cypher execution via em.run()
  • ✅ Aggregations via em.aggregate()
  • ✅ Transaction support (commit, rollback)
  • ✅ Schema operations (clearDatabase, ensureDatabase)
  • ✅ Relationship entity querying
  • ✅ Neo4j Integer conversion
  • ✅ Error handling and exception conversion

Test Coverage:

  • 100+ test cases covering all major features
  • Integration tests with real Neo4j instance (via Docker)
  • Edge cases and error scenarios

Documentation:

New Documentation (docs/docs/usage-with-neo4j.md):

  • 📖 Installation and setup
  • 📖 Entity definition with @Node(), @Field(), @Rel()
  • 📖 Relationship modeling (many-to-one, many-to-many, self-referencing)
  • 📖 Relationship properties with @RelationshipProperties()
  • 📖 Query patterns (CRUD, query builder, raw Cypher)
  • 📖 Virtual entities (static and dynamic)
  • 📖 Transaction management
  • 📖 Schema operations
  • 📖 Configuration options
  • 📖 Best practices
  • 📖 Differences from SQL drivers
  • 📖 Complete example application

Documentation Style:

  • Follows existing MikroORM documentation patterns
  • Modeled after usage-with-mongo.md
  • Comprehensive code examples
  • Clear explanations of graph concepts

Configuration Example:

import { MikroORM, defineNeo4jConfig } from '@mikro-orm/neo4j';

const config = defineNeo4jConfig({
  entities: [Person, Movie, Actor, ActedIn],
  dbName: 'neo4j',
  clientUrl: 'neo4j://localhost:7687',
  user: 'neo4j',
  password: 'password',
  driverOptions: {
    maxConnectionPoolSize: 100,
    connectionAcquisitionTimeout: 60000,
  },
});

const orm = await MikroORM.init(config);

Dependencies:

  • neo4j-driver (^5.25.0) - Official Neo4j JavaScript driver
  • @neo4j/cypher-builder (^1.2.3) - Official Cypher query builder

Future Enhancements:

Potential future improvements (not included in this PR):

  • Index and constraint management via Schema Generator
  • Entity generator from existing Neo4j graphs
  • Advanced Cypher DSL features
  • Graph algorithms integration
  • Spatial data type support
  • Full-text search integration
  • Multi-database support (Neo4j 4.0+)

Migration Path:

For users wanting to adopt Neo4j:

  1. Install the package:

    npm install @mikro-orm/neo4j neo4j-driver @neo4j/cypher-builder
  2. Update configuration:

    import { MikroORM } from '@mikro-orm/neo4j';
  3. Add graph decorators:

    @Entity()
    @Node()
    class MyEntity { ... }
  4. Use as normal:

    const entity = em.create(MyEntity, { ... });
    await em.flush();

Comparison with Other Drivers:

Feature SQL MongoDB Neo4j
Data Model Relational Document Graph
Relationships Foreign Keys Embedded/Referenced First-class
Query Language SQL MongoDB Query Cypher
Transactions
Migrations ❌ (schemaless)
Entity Generator ❌ (future)
Schema Validation ❌ (schemaless)
Query Builder

Related Issues/Discussions:


This PR represents a significant expansion of MikroORM's capabilities, bringing professional graph database support to the ecosystem while maintaining the familiar and beloved MikroORM API patterns.

@B4nan
Copy link
Member

B4nan commented Dec 18, 2025

Thanks for the PR, but I won't be accepting new drivers to v6 at this point. More importantly, this can be a 3rd party driver, I'd suggest you publish it yourself and keep it in its own repository.

@Manuel-Antunes
Copy link
Author

Manuel-Antunes commented Dec 18, 2025

Sure, can you at least include in readme, to make it easier for the people to find, like a "cool 3rd party integrations" or something like that?

@B4nan
Copy link
Member

B4nan commented Dec 18, 2025

Yeah, I don't mind linking from readme and the docs.

And if the driver ends up being popular, maybe it will find its way to the core repo eventually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants