nestjs icon indicating copy to clipboard operation
nestjs copied to clipboard

Unable to resolve dependencies of RabbitMQModule

Open kn327 opened this issue 3 years ago • 4 comments

Hi, I have created a core lib for my project which imports the RabbitMQModule, configures it and makes it available to downstream services.

I'm running this version:

    "@golevelup/nestjs-rabbitmq": "^3.1.0",

The module looks like this:

import { Global, Module, DynamicModule } from '@nestjs/common';
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';

// internal
import { AmqpService } from './services';
import { AmqpOptions, RABBITMQ_SERVICE_NAME } from './config';
import { AmqpExchange } from './enums';

@Global()
@Module({})
export class AmqpModule {
  static forRoot(options: AmqpOptions): DynamicModule {
    const module: DynamicModule = {
      global: true,
      module: AmqpModule,
      providers: [AmqpService],
      imports: [],
      exports: [AmqpService]
    };

    module.imports.push(
      RabbitMQModule.forRoot(RabbitMQModule, {
          ...options,
          exchanges: [
              ...(options.exchanges || []),
              {
                  name: AmqpExchange.EVT,
                  type: 'topic',
              },
          ],
          connectionInitOptions: {
              ...(options.connectionInitOptions || {}),
              wait: true,
          },
          enableControllerDiscovery: true
      }),
      AmqpModule
    );

    module.providers.push({
      provide: RABBITMQ_SERVICE_NAME, // used to identify the service across the microservices
      useValue: options.service_name,
    });

    return module;
  }
}

my service looks like this:

import { Injectable, Inject } from '@nestjs/common';
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';

import {
  AmqpExchange,
  AmqpQueueDirection,
  TseAction,
} from "../enums";
import {
  TseEntity,
  TseEvent,
} from '../interfaces';
import { RABBITMQ_SERVICE_NAME } from '../config';

@Injectable()
export class AmqpService {
  constructor(
    private amqpConnection: AmqpConnection,
    @Inject(RABBITMQ_SERVICE_NAME)
    private ServiceName: string,
  ) {
  }

  // logs missing amqp connection in case injection fails
  amqp(): AmqpConnection {
    if (this.amqpConnection) return this.amqpConnection;

    // REVIEW: https://github.com/golevelup/nestjs/issues/430
    // Must pin v2.2.0
    console.error(
      'AmqpModule: AMQP provider is null in config, but service is initialized - verify @golevelup/nestjs-rabbitmq npm package is on v2.2.0',
    );
  }

  /**
   * This method will serve as a common wrapper for creating new technical system events in the system
   * @param name
   * @param action
   * @param entity
   * @param content
   */
  public async TSE<T>(
    name: string,
    action: TseAction | string,
    entity: TseEntity,
    content: T,
  ) {
    try {
      await this.TseInternal<T>({
        Entity: entity,
        Content: content,
        RoutingKey: `${this.ServiceName}.${name}.${action}`,
        Exchange: AmqpExchange.EVT,
        CreatedAt: new Date(),
      });
    } catch (e) {
      console.error('Unable to publish TSE', e);
    }
  }

  private async TseInternal<T>(e: TseEvent<T>) {
    await this.amqp().publish(e.Exchange, e.RoutingKey, e);
  }
}

export function QueueName(
  service: string,
  direction: AmqpQueueDirection,
  disambiguator?: string,
) {
  if (disambiguator) return `${service}.${direction}.${disambiguator}`;

  return `${service}.${direction}`;
}

I followed the documentation provided to initialize it but I'm running into a bit of an issue getting the project running I'm facing this error message:

[Nest] 31779  - 07/24/2022, 11:57:28 AM   ERROR [ExceptionHandler] Nest can't resolve dependencies of the RabbitMQModule (DiscoveryService, ?, RabbitRpcParamsFactory, AmqpConnectionManager). Please make sure that the argument ExternalContextCreator at index [1] is available in the RabbitMQModule context.

Potential solutions:
- If ExternalContextCreator is a provider, is it part of the current RabbitMQModule?
- If ExternalContextCreator is exported from a separate @Module, is that module imported within RabbitMQModule?
  @Module({
    imports: [ /* the Module containing ExternalContextCreator */ ]
  })

Error: Nest can't resolve dependencies of the RabbitMQModule (DiscoveryService, ?, RabbitRpcParamsFactory, AmqpConnectionManager). Please make sure that the argument ExternalContextCreator at index [1] is available in the RabbitMQModule context.

Potential solutions:
- If ExternalContextCreator is a provider, is it part of the current RabbitMQModule?
- If ExternalContextCreator is exported from a separate @Module, is that module imported within RabbitMQModule?
  @Module({
    imports: [ /* the Module containing ExternalContextCreator */ ]
  })

    at Injector.lookupComponentInParentModules (/Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/injector/injector.js:231:19)
    at Injector.resolveComponentInstance (/Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/injector/injector.js:184:33)
    at resolveParam (/Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/injector/injector.js:106:38)
    at async Promise.all (index 1)
    at Injector.resolveConstructorParams (/Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/injector/injector.js:121:27)
    at Injector.loadInstance (/Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/injector/injector.js:52:9)
    at Injector.loadProvider (/Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/injector/injector.js:74:9)
    at async Promise.all (index 0)
    at InstanceLoader.createInstancesOfProviders (/Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/injector/instance-loader.js:44:9)
    at /Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/injector/instance-loader.js:29:13
    at async Promise.all (index 8)
    at InstanceLoader.createInstances (/Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/injector/instance-loader.js:28:9)
    at InstanceLoader.createInstancesOfDependencies (/Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/injector/instance-loader.js:18:9)
    at /Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/nest-factory.js:96:17
    at Function.asyncRun (/Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/errors/exceptions-zone.js:22:13)
    at NestFactoryStatic.initialize (/Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/nest-factory.js:94:13)
    at NestFactoryStatic.create (/Users/kom/Projects/DriverFly/driverfly-backend/node_modules/@nestjs/core/nest-factory.js:37:9)
    at bootstrap (/Users/kom/Projects/DriverFly/driverfly-backend/src/main.ts:28:15)

Any ideas what I might be doing incorrectly here ?

kn327 avatar Jul 24 '22 10:07 kn327

@kn327 Do you have multiple nestjs versions installed locally? Also are you running on v8? Your code looks fine to me, usually the external context creator is reported when there are multiple versions or just a simple mismatch

underfisk avatar Jul 28 '22 11:07 underfisk

@underfisk , The nestjs versions were identical and I am running v8 for the common. Here are the package versions I'm using

    "@golevelup/nestjs-rabbitmq": "^3.1.0",
    "@nestjs/common": "^8.4.7",

I was able to get a workaround for the issue by creating a custom provider for the AMQP connection which appears to work correctly:

    module.providers.push({
      provide: AmqpConnection,
      useFactory: async () => {
        const connection = new AmqpConnection(options);

        await connection.init();

        return connection;
      },
    });

I noticed the issue only happens when I try to create a global module wrapper as a sub-module of a sub-module. My architecture is as follows:

  • AppRootModule (the microservice running the application) ** ServiceModule1 (some sub-service of the microservice) ** ...Other various microservice-specific modules ** DomainCommonModule (A common module initializing all public services shared by all of my microservices) - exported as global *** AmqpModule (my wrapper for the go-level-up rabbitMQ Module) - exported as global *** ...Other various common modules - exported as global

If I initialize the AmqpModule directly in ServiceModule1 which needs it, there is no problem. However, if I initialize the AmqpModule in either the AppRootModule or the DomainCommonModule, the dependency problem occurs

kn327 avatar Aug 02 '22 19:08 kn327

I have the same error when using the library with PNPM / monorepos.

@golevelup/nestjs-rabbitmq uses ExternalContextCreator from @nestjs/core but does not define it as its own dependency. As such it is not available directly and fails when being build.

I was able to verify that using pnpm patch/patch-commit and adding the following to @golevelup/[email protected] package.json:

  "dependencies": {
    // ...
    "@nestjs/core": "^9.0.5"
  },

IMHO this is flaw, a library should always be explicit about the dependencies it uses directly, but currently @nestjs/... dependencies are used directy without specifying them.

buffcode avatar Aug 19 '22 09:08 buffcode

Addendum: more dependencies are missing and all @golevelup packages have incomplete or obsolete dependencies:

# common>npx depcheck
Missing dependencies
* @nestjs/common: .\src\injectDecoratorFactory.ts
* lodash: .\src\options.ts

#graphql-request>npx depcheck
Unused dependencies
* @golevelup/nestjs-discovery
Missing dependencies
* @nestjs/common: .\src\graphql-request.module.ts
* @nestjs/testing: .\src\test\graphql-request.module.spec.ts

# hasura>npx depcheck
Unused dependencies
* @hasura/metadata
Unused devDependencies
* ts-toolbelt
Missing dependencies
* @nestjs/common: .\src\hasura.decorators.ts
* express: .\src\hasura.event-handler.guard.ts
* rxjs: .\src\hasura.event-handler.guard.ts
* lodash: .\src\hasura.metadata.ts
* @nestjs/core: .\src\hasura.module.ts
* @nestjs/testing: .\src\tests\hasura.event-handling.spec.ts
* supertest: .\src\tests\hasura.event-handling.spec.ts

# modules>npx depcheck
Unused dependencies
* lodash

# rabbitmq>npx depcheck
Missing dependencies
* reflect-metadata: .\src\rabbitmq.decorators.ts
* @nestjs/common: .\src\rabbitmq.decorators.ts
* lodash: .\src\rabbitmq.decorators.ts
* @nestjs/core: .\src\rabbitmq.module.ts
* rxjs: .\src\amqp\connection.ts

# stripe>npx depcheck
Missing dependencies
* @nestjs/common: .\src\stripe.decorators.ts
* @nestjs/core: .\src\stripe.module.ts
* lodash: .\src\stripe.module.ts
* @nestjs/testing: .\src\tests\stripe.module.spec.ts
* supertest: .\src\tests\stripe.webhook.e2e.spec.ts

# testing>npx depcheck
Missing dependencies
* @nestjs/common: .\src\mocks.spec.ts
* @nestjs/testing: .\src\mocks.spec.ts

# webhooks>npx depcheck
Missing dependencies
* @nestjs/common: .\src\webhooks.middleware.ts
* express: .\src\webhooks.middleware.ts
* @nestjs/testing: .\src\tests\configurable-raw-body.middleware.spec.ts
* supertest: .\src\tests\configurable-raw-body.middleware.spec.ts

buffcode avatar Aug 19 '22 14:08 buffcode

Hi! I'm using pnpm in monorepos environment and facing the same problem. Is there any progress regarding this issue?

ShpakovNikita avatar Sep 23 '22 12:09 ShpakovNikita

Hi! I'm using pnpm in monorepos environment and facing the same problem. Is there any progress regarding this issue?

Hey! Having the same issue Did you solve it?

gilvex avatar Jan 05 '23 23:01 gilvex

Also having this issue

jblyberg avatar Feb 08 '23 22:02 jblyberg

@WonderPanda can you take a look, please? this MP solves the problem of the library working with yarn pnp monorepo

Amatrasan avatar Jun 01 '23 16:06 Amatrasan

@WonderPanda faced with the same issue. will be pretty great, if u'll manage with it

aderihoJD avatar Jun 01 '23 16:06 aderihoJD

Still having this issue today. We are running NestJS 9.3.9 on our codebase so I have to use @golevelup/nestjs-rabbitmq": "3.7.0",

Tchekda avatar Nov 23 '23 22:11 Tchekda

@Tchekda update reflect-metadata

imbooo avatar Jan 23 '24 05:01 imbooo

At the end I bumped to NestJS 10.x and it seems to have solved the issue. At least, we are currently running with NestJS 10.2.10 and @golevelup/nestjs-rabbitmq 4.1.0

Tchekda avatar Feb 08 '24 14:02 Tchekda