Nest can't resolve dependencies of the JwtStrategy

Viewed 870

I am a newbie in NestJs world. As far as I know, I imported everything needed in the JwtStrategy. I don't know where it went wrong. Can somebody help me with this?

As far as I referred to documetation, Whenever we want to use any entity in a module, we should import that entity in the imports field in the @Module() decorator. I did it.

jwt.strategy.ts

import { Injectable, UnauthorizedException } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { Strategy, ExtractJwt } from "passport-jwt";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { UserEntity } from "src/entities/user.entity";
import { AuthPayload } from "src/common/dtos/user.dto";

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
    constructor(
        @InjectRepository(UserEntity)
        private userRepo: Repository<UserEntity>
    ) {
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            secretOrKey: process.env.SECRETKEY
        });
    }

    async validate(payload: AuthPayload): Promise<UserEntity> {
        const { username } = payload;
        const user = this.userRepo.findOne({ where: { username: username } });
        if(!user) {
            throw new UnauthorizedException();
        }
        return user;
    }
}

auth.module.ts

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

@Module({
  imports: [
    TypeOrmModule.forFeature([UserEntity]),
    JwtModule.register({
      secret: process.env.SECRETKEY,
    }),
    PassportModule.register({
      defaultStrategy: 'jwt'
    })
  ],
  providers: [AuthService, JwtStrategy],
  controllers: [AuthController],
  exports: [PassportModule, JwtStrategy]
})
export class AuthModule {}

user.entity.ts

import { Entity, Column, OneToMany, JoinTable, BeforeInsert } from "typeorm";
import { AbstractEntity } from "./abstract-entity.abstract";
import { IsEmail } from "class-validator";
import { Exclude, classToPlain } from "class-transformer";
import * as bcrypt from "bcryptjs";
import { CategoryEntity } from "./category.entity";
import { ArticleEntity } from "./article.entity";

@Entity('User')
export class UserEntity extends AbstractEntity {
    @Column({
        type: "varchar",
        length: 80
    })
    fullName: string;

    @Column({
        type: "varchar",
        unique: true
    })
    @IsEmail()
    email: string;

    @Column({
        type: "varchar",
        unique: true
    })
    username: string;

    @Column({
        type: "varchar"
    })
    @Exclude()
    password: string;

    @Column({
        default: null,
        nullable: true
    })
    avatar: string | null;

    @Column({
        type: "varchar",
        unique: true
    })
    phoneNumber: string;

    @Column({
        type: "boolean",
        default: false
    })
    isAdmin: boolean;

    @Column({
        type: "boolean",
        default: false
    })
    isStaff: boolean;

    @Column({
        type: "boolean",
        default: false
    })
    isEmailVerified: boolean;

    @OneToMany(type => CategoryEntity, category => category.createdBy)
    @JoinTable()
    categories: CategoryEntity[];

    @OneToMany(type => ArticleEntity, article => article.createdBy)
    @JoinTable()
    articles: ArticleEntity[];

    @BeforeInsert()
    async hashPassword() {
        this.password = await bcrypt.hash(this.password, 10);
    }

    async comparePassword(attempt: string): Promise<boolean> {
        return await bcrypt.compare(attempt, this.password);
    }

    toJSON(): any {
        return classToPlain(this);
    }
}

app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from "@nestjs/typeorm";
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
import { 
  DatabaseConnectionService
 } from "./utils/database-connection.service";
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { ArticlesModule } from './articles/articles.module';
import { HttpExceptionFilter } from './common/exception-filters/http-exception.filter';
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
import { CategoryModule } from './category/category.module';

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      useClass: DatabaseConnectionService
    }),
    AuthModule,
    UsersModule,
    ArticlesModule,
    CategoryModule,
  ],
  controllers: [AppController],
  providers: [
    // {
    //   provide: APP_INTERCEPTOR,
    //   useClass: ResponseInterceptor
    // },
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter
    },
    AppService
  ],
})
export class AppModule {}

database-connection.service.ts

import { Injectable } from "@nestjs/common";
import { TypeOrmOptionsFactory, TypeOrmModuleOptions } from "@nestjs/typeorm";
import { truncate } from "fs";

@Injectable()
export class DatabaseConnectionService implements TypeOrmOptionsFactory {
    createTypeOrmOptions(): TypeOrmModuleOptions {
        return {
            type: "mysql",
            host: process.env.HOST,
            port: parseInt(process.env.PORT),
            username: process.env.DB_USERNAME,
            password: process.env.DB_PASSWORD,
            database: process.env.DATABASE, 
            entities: [__dirname + '/**/*.entity{.ts,.js}'],
            synchronize: true,
            dropSchema: true,
            autoLoadEntities: true,
            logger: "simple-console"
        };
    }
}

The Error is as follows: enter image description here

2 Answers

Based on your error, somewhere you have JwtStrategy in your imports array. If you need the JwtStrategy you should instead import the AuthModule, as providers should only be in the providers array and should never be in imports.

Consider to move to Active Record pattern. All what you need to do is just to let your AbstractEntity extends BaseEntity of TypeOrm.

You can remove all typeorm features imports like:

TypeOrmModule.forFeature([UserEntity])

and all dependency injections for repository like:

@InjectRepository(UserEntity)
private userRepo: Repository<UserEntity>

Just use the entity class for querying:

const user = await UserEntity.findOne({ where: { username } });
Related