themetalfleece / neogma

Object-Graph-Mapping neo4j framework, Fully-typed with TypeScript, for easy and flexible node and relationship operations
https://themetalfleece.github.io/neogma/
MIT License
124 stars 12 forks source link

Error: Cannot ready property 'queryRunner' of undefined #7

Closed taylor009 closed 3 years ago

taylor009 commented 3 years ago

Here is my controller code to create a new Node:

`export default class JobsController {

 public createJob = async (request: express.Request, response: express.Response): Promise<void> => {
     await Jobs.createOne({
        company: request.body.company,
        title: request.body.title,
        location: request.body.location,
        salary: request.body.salary,
        seniority: request.body.seniority,
        topApplicant: request.body.topApplicant
    })
        .then(res => response.json(res))
        .catch(error => console.error(error.message));
}`
themetalfleece commented 3 years ago

Thanks for submitting this issue. Could you include the code you used for creating the Neogma instance, the Jobs model and the folder structure?

themetalfleece commented 3 years ago

It may be possible that you're importing this file before creating the Neogma instance.

This results in the error you reported:

import { JobsController } from "./src/controllers/Jobs";

export const neogma = new Neogma(
  {
    url: "bolt://localhost:7687",
    username: "neo4j",
    password: "password",
  },
  {
    logger: console.log,
    encrypted: false,
  }
);

while this doesn't:

export const neogma = new Neogma(
  {
    url: "bolt://localhost:7687",
    username: "neo4j",
    password: "password",
  },
  {
    logger: console.log,
    encrypted: false,
  }
);

import { JobsController } from "./src/controllers/Jobs";

that's why I recommend creating the Neogma instance as soon as you've run dotenv.config(), before importing anything else!

taylor009 commented 3 years ago

Thanks, @themetalfleece that worked for me.

themetalfleece commented 2 years ago

Added documentation here: https://themetalfleece.github.io/neogma-docs/docs/Models/Defining-a-Model#importing-neogma

jotamorais commented 2 years ago

@themetalfleece first of all, thank you and congrats for the library - it helps a lot.

I'm having an issue while integrating with Nest.js. I'm trying to decouple everything to keep my app "testable" but it is causing Cannot read property 'queryRunner' of undefined error by the time the code bootstrap. I tried to create the connection early, during the app bootstrap but it didn't work either - the dependency injection doesn't even have a chance to forcefully inject the connection via a module's custom provider.

What would be your suggestion for scenarios where we rely on DI to create/manage the class instances and DB connections as well?

Our scenario also requires to connect to different Neo4j instances which means different connection strings are managed dynamically (via service logic) to determine which connection string the DI needs to inject in the constructor and then to create a Neo4j connection. How can we accomplish something like that?

I will leave some snippets of the code for your reference:

app.module.ts

@Module({
    imports: [
        ConfigModule.forRoot({ isGlobal: true }),
        NeogmaModule.forRootAsync({
            imports: [ConfigModule],
            inject: [ConfigService],
            useFactory: (configService: ConfigService): Neo4jConfig => ({
                scheme: configService.get('NEO4J_SCHEME') || 'bolt+s',
                host: configService.get('NEO4J_HOST') || 'localhost',
                port: configService.get('NEO4J_PORT') || '7687',
                username: configService.get('NEO4J_USERNAME') || 'neo4j',
                password: configService.get('NEO4J_PASSWORD') || 'neo4j',
                database: configService.get('NEO4J_DATABASE') || 'default',
            })
        }),
        AutomapperModule.withMapper(),
        CommonModule,
    ]
})
export class ApplicationModule { }

neogma.service.ts

import { Driver, int } from 'neo4j-driver'
import { Injectable, Inject, OnApplicationShutdown, Logger } from '@nestjs/common';
import { NEOGMA_CONFIG, NEOGMA_DRIVER } from './neo4j.constants';
import { Neo4jConfig } from './neo4j-config.interface';
import { Neogma, QueryRunner } from 'neogma';
import { LoggerService } from '../common/provider/logger.service';

@Injectable()
export class NeogmaService implements OnApplicationShutdown {

    private readonly logger = new Logger(NeogmaService.name);

    public static neogmaConnection: Neogma;
    neogma: Neogma;
    runner: QueryRunner;

    constructor(
        @Inject(NEOGMA_CONFIG) private readonly config: Neo4jConfig,
        @Inject(NEOGMA_DRIVER) private readonly driver: Driver
    ) {
        this.logger.debug('Initialized');

        this.runner = new QueryRunner({
            driver: this.driver,
            logger: LoggerService || console.log
        });

    }

    public static async createNeogmaConnection(config: Neo4jConfig) {

        return new Neogma({
            url: `${config.scheme}://${config.host}:${config.port}` || 'bolt://192.168.33.10:7687',
            username: config.username || 'neo4j',
            password: config.password || 'Test1*'
        },
            {
                logger: console.log,
                encrypted: false,
                disableLosslessIntegers: true
            })
    }

    getDriver(): Driver {
        return this.driver;
    }

    getNeogma(): Neogma {
        NeogmaService.neogmaConnection = this.neogma;
        return this.neogma;
    }

    getConfig(): Neo4jConfig {
        return this.config;
    }

    int(value: number) {
        return int(value)
    }

    onApplicationShutdown() {
        return this.driver.close()
    }

}

neogma.module.ts

import { DynamicModule, Module, Provider } from '@nestjs/common';
import { NEOGMA_CONFIG, NEOGMA_DRIVER } from './neo4j.constants';

import { ConfigModule } from '@nestjs/config';
import { Neo4jTransactionInterceptor } from './neo4j-transaction.interceptor';
import { NeogmaService } from './neogma.service';
import { Neo4jConfig } from './neo4j-config.interface';
import { NeogmaConnection } from '../../neogma.config';

@Module({
    exports: [
        NeogmaConnection, 
        NeogmaService
    ]
})
export class NeogmaModule {

    static forRoot(config: Neo4jConfig): DynamicModule {

        return {
            module: NeogmaModule,
            global: true,
            providers: [
                {
                    provide: NEOGMA_CONFIG,
                    useValue: config,
                },
                {
                    provide: NEOGMA_DRIVER,
                    inject: [NEOGMA_CONFIG],
                    useFactory: async (factoryConfig: Neo4jConfig) => {
                        return NeogmaService.createNeogmaConnection(factoryConfig)
                    },
                },
                NeogmaService,
            ],
            exports: [
                NeogmaService,
                Neo4jTransactionInterceptor,
            ]
        }
    }

    static forRootAsync(configProvider): DynamicModule {
        return {
            module: NeogmaModule,
            global: true,
            imports: [ConfigModule],

            providers: [
                NeogmaService,
                {
                    provide: NEOGMA_CONFIG,
                    ...configProvider
                } as Provider<any>,
                {
                    provide: NEOGMA_DRIVER,
                    inject: [NEOGMA_CONFIG],
                    useFactory: async (config: Neo4jConfig) => NeogmaService.createNeogmaConnection(config),
                },
            ],
            exports: [
                NeogmaService,
            ]
        }
    }

}
themetalfleece commented 2 years ago

@jotamorais Hey Jonathas, thanks for your message and the code snippets. This is very interesting and I want to try Neogma with Nest.js and provide a guide and a module for it. This is gonna take a bit though, but I'll let you know as soon as I have news, or if I need any more info. Thanks!

jotamorais commented 2 years ago

@jotamorais Hey Jonathas, thanks for your message and the code snippets. This is very interesting and I want to try Neogma with Nest.js and provide a guide and a module for it. This is gonna take a bit though, but I'll let you know as soon as I have news, or if I need any more info. Thanks!

No worries. That would be much appreciated. Let me know if i can be of any help.