Unable to resolve dependencies of RabbitMQModule
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 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 , 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
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.
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
Hi! I'm using pnpm in monorepos environment and facing the same problem. Is there any progress regarding this issue?
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?
Also having this issue
@WonderPanda can you take a look, please? this MP solves the problem of the library working with yarn pnp monorepo
@WonderPanda faced with the same issue. will be pretty great, if u'll manage with it
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 update reflect-metadata
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