nestjs / nest

A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript 🚀
https://nestjs.com
MIT License
67.7k stars 7.63k forks source link

DIC can not find dependancy when imported from barrel(index) file #12706

Closed CSenshi closed 1 year ago

CSenshi commented 1 year ago

Is there an existing issue for this?

Current behavior

When trying to import from current working directory nest is not able to inject provider.

import { Controller, Get } from '@nestjs/common';
import { Service } from '.'; // <- does not work
// import { Service } from './service'; // <- works

@Controller()
export class HttpController {
  constructor(private readonly a: Service) {}

  @Get()
  getHello(): string {
    return 'Hello World';
  }
}

Error: Nest can't resolve dependencies of the HttpController (?). Please make sure that the argument dependency at index [0] is available in the AppModule context.

Minimum reproduction code

https://github.com/CSenshi/nest-dic-bug-demo

Steps to reproduce

  1. npm i
  2. nest start

Expected behavior

It should just resolve dependancy.

Package

Other package

No response

NestJS version

^10.0.0

Packages versions

[System Information]
OS Version     : macOS Unknown
NodeJS Version : v20.9.0
NPM Version    : 10.1.0 

[Nest CLI]
Nest CLI Version : 10.2.1 

[Nest Platform Information]
platform-express version : 10.2.8
schematics version       : 10.0.3
testing version          : 10.2.8
common version           : 10.2.8
core version             : 10.2.8
cli version              : 10.2.1

Node.js version

20

In which operating systems have you tested?

Other

No response

micalevisk commented 1 year ago

that's due to circular imports with barrel files

Just don't import from '.'. There's no good reason to do that. It's limitation that nestjs cannot solve without using forwardref

More on this here: Circular Dependencies in NestJS and how to Avoid Them.

CSenshi commented 1 year ago
  1. The workaround is to use Injection Token (e.g. @Inject('SOME_TOKEN'). So why is nest unable to solve it? Maybe use custom injection token when DIC finds circular import?
  2. forwardRef is the solution, but it is mainly for two services including each other. Here we have circular imports but only first is using second. So maybe it is possible to use forwardRef internally when circular import is found. (circular file import and not inject)
micalevisk commented 1 year ago

So why is nest unable to solve it?

because nestjs doesn't have enough metadata to do so like you did.

You can understand it better if you understand nodejs CJS module system and typescript. That's not really related to nestjs itself.

Also, I'd suggest you to read this: https://github.com/nestjs/docs.nestjs.com/pull/2481/files to understand how @Injectable() works. Might be helpful


forwardRef is the solution

I don't think so. Most of the time it's an workaround for a design problem on your side

CSenshi commented 1 year ago

Okay I found out about something. You can actually do this. You just need to include file that uses circular DI last in the barrel file.

Before:

export * from './controller';
export * from './service';

After:

export * from './service';
export * from './controller';

As far as controller uses service and not vice versa this works