Gynteniuxas
Gynteniuxas

Reputation: 7103

NestJs tries to resolve dependencies when using enum from other module

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:

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

Answers (1)

Jay McDoniel
Jay McDoniel

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:

Feature A Module

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 {}

Feature A Service

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 );
  }
}

Feature B Module

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 {}

Feature B Service

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

Related Questions