Neogma Object-Graph Mapper

repository·main·Indexed 41 days ago

https://github.com/themetalfleece/neogma

A type-safe Object-Graph Mapper (OGM) for Neo4j built with TypeScript. Neogma provides a balance between high-level convenience for CRUD and eager loading and low-level control via a Query Builder and raw Cypher. It supports TC39 Stage 3 decorators, legacy experimental decorators, and plain JavaScript via a ModelFactory API. Version 2.0.2 includes features for schema validation using TypeBox, relationship management, and a dedicated integration package for NestJS (@neogma/nest).

Tokens
61.2K
Snippets
231
Records
308
Agent score
96%

What's inside Neogma

  1. Introduction to Neogma

    main

    Neogma is a fully type-safe Object-Graph-Mapping (OGM) framework designed for Neo4j and TypeScript. It allows developers to define graph models using decorators, manage complex relationships automatically, and build queries using a fluent API while maintaining complete type safety through automatic type inference.

    Key capabilities include:

    • Decorator-based models: Define nodes, properties, and relationships using @Node, @Property, and @Relationship.
    • Type-safe validation: Uses TypeBox for powerful schema validation (e.g., unions, patterns, constraints).
    • Eager loading: Load nested relationships in a single query to prevent N+1 performance issues.
    • Parameterized queries: Protects against injection attacks by passing all user input as parameters.
    • NestJS integration: Native support via the @neogma/nest module.
  2. Features demonstrated in the TC39 Decorator example

    main

    The basic-app-decorators example serves as a comprehensive reference for Neogma's capabilities. It sequentially demonstrates:

    1. Validation: Schema validation using TypeBox via Model.build() and validate().
    2. Create: Using createOne (including nested related-node creation) and createMany for bulk operations.
    3. Read: Using findOne, findMany with operators (e.g., Op.gte, Op.in, Op.contains), ordering, paging, and eager-loaded relationships.
    4. Update: Instance-level save() with change tracking, static Model.update(), and instance methods.
    5. Relationships: Managing connections via relateTo (static and instance), findRelationships, createRelationship, and deleteRelationships.
    6. QueryBuilder: Performing typed read queries, optional matches, and mutation chains (merge, set, unwind, call, remove, delete) within rollback transactions.
    7. Sessions & Transactions: Managing lifecycle via getSession and getTransaction (including commit and rollback paths).
    8. Delete: Using instance delete() and static Model.delete() with count.
    9. Cleanup: Removing all example nodes.
  3. Core features demonstrated in the JavaScript example

    main

    This example serves as a comprehensive reference for the following Neogma capabilities:

    • Validation: Schema validation via Model.build() and validate() using TypeBox.
    • CRUD Operations:
      • Create: createOne (with nested relationships) and createMany.
      • Read: findOne, findMany (using Op operators, ordering, paging, and eager loading).
      • Update: Instance save() with change tracking and static Model.update().
      • Delete: Instance delete() and static Model.delete().
    • Relationships: Managing connections via relateTo, findRelationships, createRelationship, and deleteRelationships.
    • QueryBuilder: Performing typed read queries, optional matches, and mutation chains.
    • Sessions & Transactions: Managing state with getSession and getTransaction (including commit/rollback).
  4. Configure relationship direction

    main

    The direction option in the @Relationship decorator determines the direction of the relationship from the perspective of the current node:

    • 'out': The relationship goes from this node to the target: (Node)-[:REL]->(Target)
    • 'in': The relationship goes from the target to this node: (Node)<-[:REL]-(Target)
    • 'none': No direction is specified: (Node)-[:REL]-(Target)
  5. Configure Decorator Styles

    main

    Neogma supports two decorator styles.

    1. TC39 decorators (default): Recommended. Requires no tsconfig.json changes. Import from neogma.
    2. Legacy experimental decorators: Requires experimentalDecorators: true and emitDecoratorMetadata: true in your tsconfig.json. Import from neogma/legacy.
    // tsconfig.json for legacy decorators
    {
      "compilerOptions": {
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true
      }
    }
  6. How transactions roll back on error

    main

    When using neogma.getTransaction, any error thrown inside the callback triggers an automatic rollback of all operations performed within that transaction block.

    try {
      await neogma.getTransaction(async (tx) => {
        await Users.createOne({ id: '1', name: 'Alice' }, { session: tx });
        throw new Error('Something went wrong');
        // Transaction is automatically rolled back
      });
    } catch (error) {
      console.log('Transaction rolled back:', error.message);
    }
  7. Handle circular dependencies in model registration

    main

    Neogma handles circular dependencies (e.g., User -> Order -> User) automatically through deferred relationship resolution.

    To ensure this works:

    1. Use the arrow function syntax for the model option in @Relationship (e.g., model: () => OtherNode). This enables lazy resolution.
    2. Models can be registered in any order using neogma.model(ModelClass). Neogma will queue relationships for models that haven't been registered yet and patch them once the target model is registered.
  8. Define bidirectional and self-referencing relationships

    main

    Bidirectional Relationships

    To navigate a relationship in both directions, define the relationship on both participating models using opposite directions ('out' and 'in').

    Self-referencing Relationships

    To create a relationship from a node to another node of the same type (e.g., a parent category to child categories), use 'self' as the model value in the @Relationship decorator.

    // Bidirectional example
    @Relationship({ name: 'PLACED', direction: 'out', model: () => OrderNode })
    Orders!: Related<typeof OrderNode>;
    
    // ... in OrderNode
    @Relationship({ name: 'PLACED', direction: 'in', model: () => UserNode })
    PlacedBy!: Related<typeof UserNode>;
    
    // Self-referencing example
    @Relationship({
      name: 'PARENT_OF',
      direction: 'out',
      model: 'self',
    })
    Children!: Related<typeof CategoryNode>;
  9. Share BindParam between parent and subqueries

    main

    When using subqueries (e.g., via the .call() method), the subquery must share the same BindParam instance as the parent QueryBuilder. This ensures that parameter names are consistent and prevents collisions between the parent and subquery scopes.

    const bp = new BindParam({ minAge: 18 });
    
    // Pass the same bp instance to the subquery
    const subquery = new QueryBuilder(bp)
      .match({ identifier: 'o', model: Orders })
      .return('o');
    
    const qb = new QueryBuilder(bp)
      .match({ identifier: 'u', model: Users })
      .call(subquery)
      .return('u, o');
  10. Use Related<T> for type-safe relationship inference

    main

    When defining relationships in a model, use the Related<typeof Model> type helper to ensure type safety for the related entities. This allows TypeScript to correctly infer the shape of the related nodes when accessing them through the relationship property.

    import type { Related } from 'neogma';
    
    // Inside a class definition:
    @Relationship({ name: 'HAS_ORDER', direction: 'out', model: () => OrderNode })
    Orders!: Related<typeof OrderNode>;
  11. Choose the correct Neogma decorator import path

    main

    Neogma supports two decorator styles. The choice depends on your project's TypeScript configuration:

    1. neogma (TC39 Stage 3 decorators): Use this for standalone projects that do not use experimentalDecorators: true.
    2. neogma/legacy (Legacy/experimental decorators): Use this for NestJS, Angular, or any project where experimentalDecorators: true is enabled in tsconfig.json.

    Both styles use the same API (@Node, @Property, @Relationship, NodeEntity) and result in identical models via toModel().