NestJS Dependency injection fails for imported modules - node.js

I inject 3 services in my controller: one is declared as provider inside the controller's module (AnswerModule), the 2 other services are declared in other modules (QuizesModule and QuestionsModule). I import these modules in AnswerModule.
Here the error
Error: Nest can't resolve dependencies of the AnswersController (AnswersService, ?, QuestionsService). Please make sure that the argument QuizesService at index [1] is available in the AnswersModule context.
Potential solutions:
- If QuizesService is a provider, is it part of the current AnswersModule?
- If QuizesService is exported from a separate #Module, is that module imported within AnswersModule?
#Module({
imports: [ /* the Module containing QuizesService */ ]
})
Here are the modules:
QuizesModule
#Module({
imports: [ AuthModule,
MongooseModule.forFeature([{ name: 'Quiz', schema: QuizSchema }]),
],
controllers: [QuizesController],
providers: [QuizesService],
})
export class QuizesModule {}
QuestionsModule:
#Module({
imports: [QuizesModule, MongooseModule.forFeature([{ name: 'Question', schema: QuestionSchema }])],
controllers: [QuestionsController],
providers: [QuestionsService],
exports: [QuizesModule]
})
export class QuestionsModule {}
AnswerModule
#Module({
imports: [QuizesModule, QuestionsModule, MongooseModule.forFeature([{ name: 'Answer', schema: AnswerSchema }])],
controllers: [AnswersController],
providers: [AnswersService,]
})
export class AnswersModule {}
Stacktrace:
Error: Nest can't resolve dependencies of the AnswersController (AnswersService, ?, QuestionsService). Please make sure that the argument QuizesService at index [1] is available in the AnswersModule context.
Potential solutions:
- If QuizesService is a provider, is it part of the current AnswersModule?
- If QuizesService is exported from a separate #Module, is that module imported within AnswersModule?
#Module({
imports: [ /* the Module containing QuizesService */ ]
})
at Injector.lookupComponentInParentModules (/Users/laurent/Projects/perso/nestjs-elearning/node_modules/#nestjs/core/injector/injector.js:191:19)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async Injector.resolveComponentInstance (/Users/laurent/Projects/perso/nestjs-elearning/node_modules/#nestjs/core/injector/injector.js:147:33)
at async resolveParam (/Users/laurent/Projects/perso/nestjs-elearning/node_modules/#nestjs/core/injector/injector.js:101:38)
at async Promise.all (index 1)
at async Injector.resolveConstructorParams (/Users/laurent/Projects/perso/nestjs-elearning/node_modules/#nestjs/core/injector/injector.js:116:27)
at async Injector.loadInstance (/Users/laurent/Projects/perso/nestjs-elearning/node_modules/#nestjs/core/injector/injector.js:80:9)
at async Injector.loadController (/Users/laurent/Projects/perso/nestjs-elearning/node_modules/#nestjs/core/injector/injector.js:28:9)
at async Promise.all (index 0)
at async InstanceLoader.createInstancesOfControllers (/Users/laurent/Projects/perso/nestjs-elearning/node_modules/#nestjs/core/injector/instance-loader.js:51:9)
at async /Users/laurent/Projects/perso/nestjs-elearning/node_modules/#nestjs/core/injector/instance-loader.js:29:13
at async Promise.all (index 12)
at async InstanceLoader.createInstances (/Users/laurent/Projects/perso/nestjs-elearning/node_modules/#nestjs/core/injector/instance-loader.js:26:9)
at async InstanceLoader.createInstancesOfDependencies (/Users/laurent/Projects/perso/nestjs-elearning/node_modules/#nestjs/core/injector/instance-loader.js:16:9)
at async /Users/laurent/Projects/perso/nestjs-elearning/node_modules/#nestjs/core/nest-factory.js:81:17
at async Function.asyncRun (/Users/laurent/Projects/perso/nestjs-elearning/node_modules/#nestjs/core/errors/exceptions-zone.js:17:13)
Thanks for your help, I do not understand my settings in modules seem correct
Laurent

exports: [QuizesService], at QuizesModule
The AnswersModule uses this service. So we need to expose it.

Thanks for your answer now it works
Here is the fix... I export QUizesService in QuizesModule, I export AuthModule in QUizesModule, I export QuestionsService in QuestionsModule
#Module({
imports: [ AuthModule,
MongooseModule.forFeature([{ name: 'Quiz', schema: QuizSchema }]),
],
controllers: [QuizesController],
providers: [QuizesService],
//...quizesProviders, ...usersProviders]
exports: [QuizesService, AuthModule]
})
export class QuizesModule {}
QUestionsModule
#Module({
imports: [QuizesModule, MongooseModule.forFeature([{ name: 'Question', schema: QuestionSchema }])],
controllers: [QuestionsController],
providers: [QuestionsService],
exports: [QuestionsService]
})
export class QuestionsModule {}

Related

Nest JS's Throttler Guard from REST module is breaking GraphQL module

I have a Nest.js app with a REST module and a GraphQL module. Both are imported into an App.module.ts. I'm using Nest's Throttler Guard to protect the whole application. As it's already known, GraphQL does not work with the normal ThrottlerGuard, so I created a GqlThrottlerGuard and imported it on the GraphQL module, while importing the original ThrottlerGuard on the REST module.
So, my graphQL module looks like this:
#Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: true
}),
ThrottlerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
ttl: config.get('security.throttle.ttl'),
limit: config.get('security.throttle.limit'),
}),
}),
],
providers: [
{
provide: APP_GUARD,
useClass: GqlThrottlerGuard,
},
],
})
export class GraphModule { }
And the REST module, like this:
#Module({
imports: [
ThrottlerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
ttl: config.get('security.throttle.ttl'),
limit: config.get('security.throttle.limit'),
}),
}),
],
controllers: [RestController],
providers: [
{
provide: APP_GUARD,
useClass: ThrottlerGuard,
},
],
})
export class RestModule implements NestModule {}
Finally, both modules are imported to the App Module, which is the module I actually run:
#Module({
imports: [
RestModule,
GraphModule,
],
})
export class AppModule { }
For some reason, the error seen here is still happening to me on the GraphModule, even though the normal ThrottlerGuard is imported only on the RestModule. Should it work like this? How can I solve it?
APP_GUARD is a global binding, it applies to all routes. You should make one coherent guard that returns the proper request response based on the ExecutionContext#getType method which will either return http or graphql
#Injectable()
export class CustomThrottlerGuard extends ThrottlerGuard {
getRequestResponse(context: ExecutionContext) {
const reqType = context.getType<ContextType | 'graphql'>()
if (reqType === 'graphql') {
const gqlCtx = GqlExecutionContext.create(context);
const ctx = gqlCtx.getContext();
return { req: ctx.req, res: ctx.res };
} else if (reqType === 'http') {
return {
req: context.switchToHttp().getRequest(),
res: context.switchToHttp().getResponse()
}
} else {
// handle rpc and ws if you have them, otherwise ignore and make previous `else if` just an `else`
}
}

nestjs process.env.variable is undefined in my auth.module.ts

im trying to test jwt auth in nestjs.
when i called jwtService.sign();
it shows error secretOrPrivateKey must have a value - {}
secret is undefined.
but in AuthController, porcess.env.JWT_SECRET_KEY is work.
i dont know why it is not work.
how can i fix it ?
auth.module.ts
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [AppConfig, MySqlConfig, OracleConfig],
envFilePath: `${process.env.NODE_ENV}` == '' ? '.env.dev' : `.env.${process.env.NODE_ENV}`,
validationSchema: Joi.object({
NODE_ENV: Joi.string()
.valid('dev', 'stg', 'prd'),
}),
}),
AuthModule,
],
controllers: [AppController],
providers: [AppService, Logger],
})
export class AppModule {}
app.module.ts
#Module({
imports: [
PassportModule,
JwtModule.register({
secret: process.env.JWT_SECRET_KEY,
signOptions: { expiresIn: '1d' },
}),
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy, LocalStrategy],
exports: [AuthService],
})
export class AuthModule {}
main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
bufferLogs: true,
});
app.enableShutdownHooks();
app.useLogger(app.get(MyLogger));
const configService = app.get(ConfigService);
const logger = app.get(MyLogger);
const config = new DocumentBuilder()
.setTitle('nestjs-tst-boilerplate')
.setDescription('The nestjs-tst-boilerplate API description')
.setVersion('0.0.1')
.addTag('tag')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(configService.get<number>('app.port'));
logger.log(`Application running on [${configService.get<string>('app.env')}] enviroment. ${await app.getUrl()}`);
}
bootstrap();
at the time that process.env.JWT_SECRET_KEY is read, the .env might not be parsed yet. Thus, don't rely on process.env. if you're using some module like #nestjs/config. Use the async version of JwtModule.register instead and inject the ConfigService. See: https://github.com/nestjs/jwt/blob/master/README.md#async-options

NestJs monorepo shared lib injection

I have NestJS application with couple microservices stored in single repository (monorepo approach).
AccessControl module stores in libs, it should be shared across multiple microservices. It has AccessControlModule.ts file
#Global()
#Module({
providers: [
{
provide: 'CONNECTION1',
useFactory: (configService: ConfigService) => {
return ClientProxyFactory.create(
configService.getRMQConnection(),
);
},
inject: [ConfigService],
},
ACGuard,
],
exports: [ACGuard],
imports: [ConfigModule],
})
export class AccessControlModule implements OnModuleDestroy {
constructor(
#Inject('CONNECTION1')
protected readonly orgConnection: ClientProxy,
) {}
onModuleDestroy(): any {
this.orgConnection.close();
}
}
This file responsible for module description, it creates connection for another microservice and provide it to ACGuard service. ACGuard.ts:
#Injectable()
export class ACGuard implements CanActivate {
constructor(
private readonly reflector: Reflector,
private config: ConfigService,
#Inject('CONNECTION1')
private readonly userConnection: ClientProxy;
) {}
public async canActivate(context: ExecutionContext): Promise<boolean> {
// do some stuff
}
}
This part by itself compiles well and logically works fine. Problem begins when I try to inject it into one of microservices. I do it as usual by adding AccessControlModule into import part of some module. For example KioskModule:
#Module({
imports: [
...
AccessControlModule
],
providers: [
...
KiosksResolver
]
})
export class KiosksModule {}
Since AccessControlModule marked as Global and exports ACGuard I expect it to be injectable into my providers.
#Resolver('Kiosk')
export class KiosksResolver {
...
#UseGuards(ACGuard)
#Query()
kiosks() {
// ...
}
...
}
But this code falls on the compilation step with error:
[Nest] 9964 - 05/07/2020, 9:33:02 PM [ExceptionHandler] Nest can't resolve dependencies of the ACGuard (Reflector, ConfigService, ?). Please make sure that the argument CONNECTION1 at index [2] is available in the KiosksModule context.
On the other hand, if i inject it in KiosksResolver's constructor, application builds successfully.
I will appreciate any help and ideas, thanks!
The way how i solved this issue was exporting CONNECTION1 provider in AccessControlModule.ts.
#Module({
providers: [
{
provide: 'CONNECTION1',
useFactory: (configService: ConfigService) => {
return ClientProxyFactory.create(
configService.getRMQConnection(),
);
},
inject: [ConfigService],
},
ACGuard,
],
exports: [ACGuard, 'CONNECTION1'],
imports: [ConfigModule],
})
export class AccessControlModule ...
With this export KioskModule creates it's own ACGuard but provides here connection exported from AccessControlModule.
It's not clear for me why KioskModule doesn't get built instance of ACGuard exported from AccessControlModule but try build it once more.

NestJS cannot resolve my UsersService dependency why? Is it due to UserModel?

I am trying to learn NodeJS framework. I have initialized a project for that and now I am trying to add authentication to secure the HTTP access. I follows the NestJS documentation but I have the following error when I inject my UsersService in the AuthService:
[Nest] 14876 - 13/04/2020 à 09:26:19 [ExceptionHandler] Nest can't resolve dependencies of the UsersService (?). Please make sure that the argument UserModel at index [0] is available in the AuthModule context.
Potential solutions:
- If UserModel is a provider, is it part of the current AuthModule?
- If UserModel is exported from a separate #Module, is that module imported within AuthModule?
#Module({
imports: [ /* the Module containing UserModel */ ]
})
+2ms
Error: Nest can't resolve dependencies of the UsersService (?). Please make sure that the argument UserModel at index [0] is available in the AuthModule context.
It seems that it is due to the following injection in my UsersService :
constructor(#InjectModel('User') private readonly UserModel: Model) {}
but I doesn't know how to solve this problem. My training project is stored on github :
https://github.com/afontange/nest-js.git
I read other tickets on same subject but I don't know what is the solution for my problem.
Thanks for your help.
export const UsersSchema = new Schema({
name: String,
});
Module({
imports: [
MongooseModule.forFeature([{ name: 'Users', schema: UsersSchema }]) // add
],
controllers: [],
providers: [],
exports: []
})
export class AuthModule {}
Do you add in your Auth.module.ts
The AuthModule is the following:
import { Module } from '#nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '#nestjs/passport';
import { LocalStrategy } from './local.strategy';
import { UsersService } from '../users/users.service';
#Module({
imports: [UsersModule, PassportModule],
providers: [AuthService, LocalStrategy, UsersService],
})
export class AuthModule {}

Mock multiple TypeORM repositories in NestJS

I'm having trouble to mock multiple repositories from different modules in NestJS.
I'm using a UsersRepository from a UsersModule inside another module service (NotesService). The code is working fine, but I can't make the unit tests work.
I have the following error: Error: Nest can't resolve dependencies of the UserRepository (?). Please make sure that the argument Connection at index [0] is available in the TypeOrmModule context.
Minimal reproduction
// [users.module.ts]
#Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
// [users.service.ts]
#Injectable()
export class UsersService {
constructor(#InjectRepository(User) private usersRepository: UsersRepository) {}
...
}
// [notes.module.ts]
#Module({
imports: [
TypeOrmModule.forFeature([Note, User]),
UsersModule,
],
controllers: [NotesController],
providers: [NotesService],
})
export class NotesModule {
}
// [notes.service.ts]
#Injectable()
export class NotesService {
constructor(
#InjectRepository(Note) private notesRepository: NotesRepository,
#InjectRepository(User) private usersRepository: UsersRepository,
) {}
...
}
Here is my unit test configuration:
// [notes.service.spec.ts]
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [UsersModule],
providers: [
NotesService,
{ provide: getRepositoryToken(Note), useFactory: repositoryMockFactory },
{ provide: getRepositoryToken(User), useFactory: repositoryMockFactory },
],
}).compile();
notesService = module.get<NotesService>(NotesService);
notesRepositoryMock = module.get(getRepositoryToken(Note));
});
The problem is I can't make a proper mock of the UsersRepository that comes from another module.
I tried importing TypeOrmModule directly inside the test, and everything I could but I can't make it work.
You don't need to import the UsersModule if you are directly providing the NotesService and the mocks that it will depend on. The reason for the error is that Nest is trying to resolve TypeormModule.forFeature([User]) from the UsersModule. Simply remove the import in the test and you should be golden.

Resources