Nest: Can't understand modules. Service imports don't work.

Created on 27 Aug 2019  路  7Comments  路  Source: nestjs/nest

I get that I should ask on StackOverflow, but it's a weird place where none of my questions ever get answered, aside from myself.

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.

Edit:

is the auth.module.ts supposed to look like this then?

@Module({
  imports: [
    HttpModule,
    UserModule,
    PassportModule,
    JwtModule.register({ secret: process.env.JWT_SECRET }),
  ],
  // Added User Service bellow at index [1]
  providers: [AuthenticationService, UserService, LocalStrategy],
  controllers: [AuthenticationController],
  exports: [AuthenticationService],
})
export class AuthenticationModule {}

Doesn't it beat the purpose of modules?

Edit 2:

Well that didn't work.

needs triage question 馃檶

All 7 comments

Apparently the constructor needs to look like THIS:

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

Instead of:

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

Means I cannot have interfaces in the constructor. Weird flex, but ok.

@TheImpressionist more on the interface vs class thing.

From the docs:

We could determine the DTO schema by using TypeScript interfaces, or by simple classes. Interestingly, we recommend using classes here. Why? Classes are part of the JavaScript ES6 standard, and therefore they are preserved as real entities in the compiled JavaScript. On the other hand, since TypeScript interfaces are removed during the transpilation, Nest can't refer to them at runtime.

Plus further elaboration #1228

tl;dr: Not a Nest problem, it is a TS/JS problem because interfaces do not exist on run-time.

@BrunnerLivio yup, as I figured there.

@TheImpressionist I think I just answered the same question on another issue. As I mentioned there, while it's fresh in mind, I'd appreciate any feedback on what was missing/confusing in the docs.

@johnbiundo the documentation for the most part is clear. The only thing was about dependency injection, I may have missed the fact that when injecting you have to use the type of the class instead of an interface. It is mentioned in the controller section, under the DTO but I guess I couldn't figure out that it's not exclusive to the DTOs.

@TheImpressionist Thanks for the feedback. I do think it could be made more clear. We're reworking some of the DI related docs now, so I'll bear this feedback in mind and hopefully the updates will help others from tripping over the same issue.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings