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.45k stars 7.6k forks source link

[ExceptionHandler] Nest can't resolve dependencies of the UserController (?, +)... #886

Closed pirmax closed 6 years ago

pirmax commented 6 years ago

I'm submitting a...


[ ] Regression 
[X] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

Trying to use my UserService.ts and AuthService.ts in my UserController.ts, but I get the following error: [ExceptionHandler] Nest can't resolve dependencies of the UserController (?, +). Please make sure that the argument at index [0] is available in the current context.

Minimal reproduction of the problem with instructions

application.module.ts

import { Module } from "@nestjs/common";
import { ApplicationController } from "controllers/application.controller";
import { ApplicationService } from "services/application.service";
import { AuthModule } from "./auth.module";
import { UserModule } from "./user.module";

@Module({
  imports: [
    AuthModule,
    UserModule,
  ],
  controllers: [
    ApplicationController,
  ],
  providers: [
    ApplicationService,
  ],
})
export class ApplicationModule {}

user.module.ts

import { Module } from "@nestjs/common";
import { UserController } from "controllers/user.controller";

@Module({
  controllers: [
    UserController,
  ],
})
export class UserModule {}

user.service.ts

import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { UserEntity } from "entities/user.entity";

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(UserEntity)
    private readonly repository: Repository<UserEntity>,
  ) {}

  async findAll(): Promise<UserEntity[]> {
    return await this.repository.find();
  }
}

auth.module.ts

import { Module } from "@nestjs/common";
import { JwtModule } from "@nestjs/jwt";
import { AuthService } from "services/auth.service";

@Module({
  imports: [
    JwtModule.register({
      secretOrPrivateKey: "key12345",
    }),
  ],
})
export class AuthModule {}

auth.service.ts

import { Injectable } from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
import { TokenJwtInterface } from "interfaces/token-jwt.interface";

@Injectable()
export class AuthService {
  private tokenType;

  constructor(private readonly jwtService: JwtService) {
    this.tokenType = "bearer";
  }

  public generateTokenJwt(
    payload: object,
    expiresIn: number,
  ): TokenJwtInterface {
    const accessToken = this.jwtService.sign(payload);

    return {
      access_token: accessToken,
      token_type: this.tokenType,
      refresh_token: "",
      expires_in: expiresIn,
    };
  }
}

user.controller.ts

import {
  Get,
  Controller,
  Post,
  Body,
  HttpCode,
  HttpStatus,
} from "@nestjs/common";
import { UserService } from "services/user.service";
import { UserEntity } from "entities/user.entity";
import * as bcrypt from "bcryptjs";
import { AuthService } from "services/auth.service";

@Controller("/users")
export class UserController {
  constructor(
    private readonly userService: UserService,
    private readonly authService: AuthService,
  ) {}

  @Get()
  async root(): Promise<UserEntity[]> {
    return await this.userService.findAll();
  }
...

What is the motivation / use case for changing the behavior?

Bugfix?

Environment


Nest version: 5.1.0


For Tooling issues:
- Node version: v8.11.3
- Platform:  Ubuntu

Others:
IDE: VSC
ryanswart commented 6 years ago

You need to provide UserService in a module. Adding UserService to providers in application.module.ts should work

pirmax commented 6 years ago

Thanks for reply @ryanswart but I've this error now:

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

ryanswart commented 6 years ago

Check https://docs.nestjs.com/techniques/database - you need to add TypeOrmModule.forFeature([UserEntity]) to your module imports

pirmax commented 6 years ago

Now I have this files and I get this error message:

[ExceptionHandler] Nest can't resolve dependencies of the AuthService (?). Please make sure that the argument at index [0] is available in the current context.

application.module.ts

import { Module } from "@nestjs/common";
import { ApplicationController } from "controllers/application.controller";
import { ApplicationService } from "services/application.service";
import { TypeOrmModule } from "@nestjs/typeorm";
import { UserModule } from "./user.module";
import { AuthModule } from "./auth.module";
import { AuthService } from "services/auth.service";
import { UserService } from "services/user.service";
import { UserController } from "controllers/user.controller";
import { UserEntity } from "entities/user.entity";

@Module({
  imports: [
    AuthModule,
    UserModule,
    TypeOrmModule.forFeature([
      UserEntity,
    ]),
    TypeOrmModule.forRoot({
      type: "mysql",
      host: "",
      port: 3306,
      username: "",
      password: "",
      database: "",
      entities: [__dirname + "/../entities/*.entity{.ts,.js}"],
      synchronize: true,
    }),
  ],
  controllers: [
    ApplicationController,
    UserController,
  ],
  providers: [
    ApplicationService,
    UserService,
    AuthService,
  ],
})
export class ApplicationModule {}

user.module.ts

import { Module } from "@nestjs/common";
import { UserController } from "controllers/user.controller";
import { UserService } from "services/user.service";

@Module({
  controllers: [
    UserController,
  ],
  providers: [
    UserService,
  ],
})
export class UserModule {}

auth.module.ts

import { Module } from "@nestjs/common";
import { JwtModule } from "@nestjs/jwt";
import { AuthService } from "services/auth.service";

@Module({
  imports: [
    JwtModule.register({
      secretOrPrivateKey: "key12345",
    }),
  ],
  providers: [
    AuthService,
  ],
})
export class AuthModule {}

user.service.ts

import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { UserEntity } from "entities/user.entity";

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(UserEntity)
    private readonly userRepository: Repository<UserEntity>,
  ) {}

  async findAll(): Promise<UserEntity[]> {
    return await this.userRepository.find();
  }
}

auth.service.ts

import { Injectable } from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
import { TokenJwtInterface } from "interfaces/token-jwt.interface";

@Injectable()
export class AuthService {
  private tokenType;

  constructor(private readonly jwtService: JwtService) {
    this.tokenType = "bearer";
  }

  public generateTokenJwt(
    payload: object,
    expiresIn: number,
  ): TokenJwtInterface {
    const accessToken = this.jwtService.sign(payload);

    return {
      access_token: accessToken,
      token_type: this.tokenType,
      refresh_token: "",
      expires_in: expiresIn,
    };
  }
}
ryanswart commented 6 years ago

AuthModule should provide AuthService, not ApplicationModule because AuthModule imports JwtModule, which exposes JwtService. If you imported JwtModule in ApplicationModule, it would work, but that wouldn't be good separation of concerns

kgish commented 5 years ago

For those who might be interested, I ran into the same problem and was able to solve it by including TypeOrmModule.forFeature() in the imports section of auth.module.ts file.

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
import { UserService } from '../../user/user.service';
import { UserEntity } from '../../user/user.entity';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forFeature([UserEntity]),
    PassportModule.register({ defaultStrategy: 'jwt' }),
    JwtModule.register({
      secretOrPrivateKey: 'secret',
      signOptions: { expiresIn: '30m' },
    }),
  ],
  controllers: [AuthController],
  providers: [AuthService, JwtStrategy, UserService],
})
export class AuthModule {}
m-podlesny commented 5 years ago

Important note: This post is only for those who has similar problem, but not exactly the same like reporter. Related to typing.

Previously (when error occurred), I had in users.controller.ts:

// Problematic nesting in services object:
constructor(private readonly services: { users: UsersService }) { }

What was helpful for me:

// Flat variable according to documentation:
constructor(private readonly userService: UsersService) { }
alxizr commented 5 years ago

please check your app.module.ts for the controllers and providers arrays. i was using the cli to generate all the different elements and by default these arrays are updated with the new 'components'. problem is that once you create your own custom module you include these arrays and their content inside. and they are only visible in that specific scope. so if for example you have UserService element inside a services folder inside your model then this elemet is hidden from the global scope.

In my opinion its a bit fkd up because of circular dependency injection and the entire hirarcy Nestjs is build upon or maybe i am mistaken to think it this way, but heck, it solved my issue than i'm glad

DogAndHerDude commented 5 years ago

The documentation is lacking for someone like me, mentally challenged.

I cannot get any of this to work either... app.module.ts

@Module({
  imports: [
    HttpModule,
    AuthenticationModule,
    UserModule,
  ],
  controllers: [AppController],
})
export class AppModule {}

user.module.ts

@Module({
  providers: [UserService],
  exports: [UserService],
})
export class UserModule {}

auth.module.ts

@Module({
  imports: [
    HttpModule,
    UserModule,
    PassportModule,
    JwtModule.register({ secret: process.env.JWT_SECRET }),
  ],
  providers: [AuthenticationService, LocalStrategy],
  controllers: [AuthenticationController],
  exports: [AuthenticationService],
})
export class AuthenticationModule {}

auth.service.ts

@Injectable()
export class AuthenticationService implements IAuthenticationService {

  constructor(
    protected readonly userService: IUserService,
    protected readonly jwtService: JwtService,
  ) {}

  /**
   * Attempts to authenticate a user with a given email and password combination.
   * 
   * @param email 
   * @param password 
   */
  public async authenticate(email: string, password: string): Promise<Session> {
    try {
      const user = await this.userService.getAuthenticationDetails(email);

      if (isNil(user)) {
        throw new Error(NOT_FOUND_ERR);
      }

      if (!comparePassword(password, user.password, user.salt)) {
        throw new Error(MISMATCH_ERR);
      }

      return sessionFactory(user._id.toString(), user.email, user.userType);
    } catch (err) {
      throw err;
    } 
  }

  /**
   * Creates a JWT from a session
   * 
   * @param session Session object to produce the JWT out of
   */
  public signJWT(session: ISession): string {
    return this.jwtService.sign(session);
  }
}

Now, when I try to run npm start I get this:

Nest can't resolve dependencies of the AuthenticationService (?, JwtService). Please make sure that the argument at index [0] is available in the AuthenticationModule context. +16ms

The docs say that I just need to import the module. Yet nothing is working. So are the docs wrong? What do I do here? Do I need to set authentication module providers to have UserService in there? If so, what's the point of modules in that case if I can dump EVERYTHING into the main app.module.ts instead? Because this seems to be way easier.

johnbiundo commented 5 years ago

@TheImpressionist - check IUserService vs. UserService. The error indicates that Nest doesn't have an injectable associated with the token IUserService. Can't tell without knowing more, but perhaps it's simply changing the constructor line to protected readonly userService: UserService

johnbiundo commented 5 years ago

Also

The documentation is lacking for someone like me, mentally challenged.

I'm interested in addressing any shortcomings in the documentation. What information was lacking/confusing (maybe you'll know better after getting this resolved) ?

audiBookning commented 5 years ago

I'm interested in addressing any shortcomings in the documentation. What information was lacking/confusing (maybe you'll know better after getting this resolved) ?

@johnbiundo I realize that this is not really the place to discuss this (i should open an issue) but since i was browsing this issue and you just asked, so here are two quick personal suggestions about the docs:

mkelandis commented 5 years ago

Having the same issue as @TheImpressionist -- and @kgish 's solution resolves it.

@Module({
  imports: [
    UserModule,
    TypeOrmModule.forFeature([User]) // <-- why does this need to be here???
  ],
  providers: [AuthService, UserService],
  exports: [AuthService]
})
export class AuthModule {}

It seems odd to me that the dependencies of the UserModule have to also be specified in the AuthModule. Do the module's import dependencies normally have to be explicitly configured everywhere the module is used - isn't that a leaky abstraction? Or is this only an issue for the injected TypeOrmModule?

kamilmysliwiec commented 5 years ago

@mkelandis your UserModule has to export TypeOrmModule. Then, TypeOrmModule.forFeature([User]) won't be required

iansamz commented 5 years ago

I dont know if im helping anyone but in my implementation I had forgot to import HttpModule in app.module.ts. When I imported it worked fine. Hope it helped someone.

bmoe24x commented 5 years ago

In case it helps anyone, my issue was needing to put a service in the 'exports' array of it's parent module, then in the module that consumed said service I needed to include the service in the imports AND providers array. Not sure if this is best practice but it is the only thing I found that fixed my issue.

DimosthenisK commented 5 years ago

I believe that the "Can't resolve dependencies" error is way too generic. Is there any way to make it more clear as to what is not correct?

spali commented 5 years ago

@kamilmysliwiec That worked for me. But I would expect having TypeOrmModule.forRoot and even export it in the AppModule should be enough. For me that would mean, in theory I have to export TypeOrmModule in any module with resolvers/repositories that are required in another module. Possible, but not intuitive in my eyes. 🙈 I think anyway this is not a nestjs problem rather than a TypeOrm problem.