Reputation: 7103
I'm having problems understanding why enum taken into consideration while resolving dependencies.
The situation is as follows:
I have two feature folders. Let's call them FeatureA and FeatureB. FeatureA in this doesn't do much. The minimal working example for this feature is:
feature-a.module.ts:
import { Module } from '@nestjs/common';
import { FeatureAService } from './feature-a.service';
@Module({
providers: [FeatureAService],
})
export class FeatureAModule {}
feature-a.service.ts:
import { Injectable } from '@nestjs/common';
import { STUFF_B_ONLY } from '../FeatureB/feature-b.service';
@Injectable()
export class FeatureAService {
public doSomeStuff(): string {
return 'Doing stuff: ' + STUFF_B_ONLY.A; // <--- Problems with this line, enum
}
}
FeatureB uses some functionality from FeatureA. As a result, I added needed dependencies to access them.
feature-b.module.ts:
import { Module } from '@nestjs/common';
import { FeatureAService } from '../FeatureA/feature-a.service';
import { FeatureBService } from './feature-b.service';
@Module({
providers: [FeatureAService, FeatureBService],
})
export class FeatureBModule {}
feature-b.service.ts:
import { Injectable } from '@nestjs/common';
import { FeatureAService } from '../FeatureA/feature-a.service';
export enum STUFF_B_ONLY {
A = 'a',
B = 'b',
}
@Injectable()
export class FeatureBService {
constructor(private readonly featureAService: FeatureAService) {}
public do(): void {
this.featureAService.doSomeStuff();
}
}
In feature-b.service.ts I just call doSomeStuff()
from featureAService.
But the problem is: I use an enum from feature-b.service.ts
in feature-a.service.ts
and for some reason NestJs tries to resolve all dependencies even though enum is outside of @Injectable
provider and class in general. This enum is not part of featureB and shouldn't throw any errors.
Error message:
Error: Nest can't resolve dependencies of the FeatureBService (?). Please make sure that the argument dependency at index [0] is available in the FeatureBModule context.
Potential solutions:
If dependency is a provider, is it part of the current FeatureBModule?
If dependency is exported from a separate @Module, is that module imported within FeatureBModule? @Module({
imports: [ /* the Module containing dependency */ ] })
2 solutions found were:
Move enum to a generic .ts file, not even in a module, but this approach is not always the best, it could be crowded if tons of different enums will be added
Replace enum value (STUFF_B_ONLY.A
) with a basic string, but this approach is not valid for me
So, WHY NestJs tries to resolve dependencies on enum and is there something I missed (provide/inject/import)? Or moving to a generic .ts file is the only option here?
In case needed, the main module file:
import { Module } from '@nestjs/common';
import { FeatureAModule } from './FeatureA/feature-a.module';
import { FeatureBModule } from './FeatureB/feature-b.module';
@Module({
imports: [
FeatureAModule,
FeatureBModule,
],
})
export class AppModule {}
Upvotes: 0
Views: 3216
Reputation: 70490
I believe the main reason this happens is that the Nest scanner runs through the code and looks at all the imports and exports of a file, along with working with the module metadata defined in the @Module()
decorators. Here, you've created a circular dependency between the two services due to the imports and exports between them. There is a way to fix this with just modules and not needing a new file, using the forwardRef
function provided by Nest like so:
import { Module, forwardRef } from '@nestjs/common';
import { FeatureAService } from './feature-a.service';
import { FeatureBModule } from 'src/feature-b/feature-b.module';
@Module({
imports: [forwardRef(() => FeatureBModule)],
providers: [FeatureAService],
exports: [FeatureAService],
})
export class FeatureAModule {}
import { Injectable } from '@nestjs/common';
import { STUFF_FROM_B } from '../feature-b/feature-b.service';
@Injectable()
export class FeatureAService {
doStuff() {
console.log('Doing stuff:\t' + STUFF_FROM_B.A );
}
}
import { Module, forwardRef } from '@nestjs/common';
import { FeatureBService } from './feature-b.service';
import { FeatureAModule } from 'src/feature-a/feature-a.module';
@Module({
imports: [forwardRef(() => FeatureAModule)],
providers: [FeatureBService],
exports: [FeatureBService],
})
export class FeatureBModule {}
import { Injectable, Inject, forwardRef } from '@nestjs/common';
import { FeatureAService } from 'src/feature-a/feature-a.service';
export enum STUFF_FROM_B {
A = 'a',
B = 'b',
}
@Injectable()
export class FeatureBService {
constructor(@Inject(forwardRef(() => FeatureAService)) private readonly featureAService: FeatureAService) {}
doStuff(): void {
this.featureAService.doStuff();
}
}
This is not necessarily a bad practice, but in general circular dependencies should be avoided, for reasons like this one. Most of the time, Nest will warn about circular dependencies between services, but as this is between files that would explain why it wasn't quite caught as expected.
Upvotes: 2