Nest js convert module from forRoot to forRootAsync - node.js

#Module({
imports: [],
providers: [SupertokensService, AuthService],
exports: [],
controllers: [AuthController],
})
export class AuthModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthMiddleware).forRoutes('*');
}
static forRoot({
connectionURI,
apiKey,
appInfo,
}: AuthModuleConfig): DynamicModule {
return {
providers: [
{
useValue: {
appInfo,
connectionURI,
apiKey,
},
provide: ConfigInjectionToken,
},
],
exports: [],
imports: [],
module: AuthModule,
};
}
}
The problem with this implementaion I can't use env variables, so I need useFactory to pass ConfigService. Can somebody do that, and give some explanation.

I figure out how to make this work, unfortunately it only works with nest.js version 9 (current latest version). First you need to create a new file. For example auth.module-definition.ts. Now in this file we need to create ConfigurableModuleBuilder.
import { ConfigurableModuleBuilder } from '#nestjs/common';
import { AuthModuleConfig } from './config.interface';
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
new ConfigurableModuleBuilder<AuthModuleConfig>()
.setClassMethodName('forRoot')
.build();
we need to set setClassMethodName('forRoot'), when we set forRoot it will create two methods, forRoot and forRootAsync. The next step is to extend our created ConfigurableModuleClass. it should look something like this
import { MiddlewareConsumer, Module } from '#nestjs/common';
import { AuthMiddleware } from './auth.middleware';
import { SupertokensService } from './supertokens/supertokens.service';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { ConfigurableModuleClass } from './auth.module-definition';;
#Module({
imports: [],
providers: [SupertokensService, AuthService],
controllers: [AuthController],
exports: [AuthService],
})
export class AuthModule extends ConfigurableModuleClass {
configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthMiddleware).forRoutes('*');
}
}
And that's actually it, so from now on we have forRootAsync and we get reigister it in app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '#nestjs/common';
import { AuthModule } from './auth/auth.module';
import { ConfigModule, ConfigType } from '#nestjs/config';
import authConfig from './auth/auth.config';
#Module({
imports: [
AuthModule.forRootAsync({
inject: [authConfig.KEY],
imports: [ConfigModule.forFeature(authConfig)],
useFactory: (config: ConfigType<typeof authConfig>) => {
return {
connectionURI: config.CONNECTION_URI,
appInfo: {
appName: config.appInfo.APP_NAME,
apiDomain: config.appInfo.API_DOMAIN,
websiteDomain: config.appInfo.WEBSITE_DOMAIN,
apiBasePath: config.appInfo.API_BASE_PATH,
websiteBasePath: config.appInfo.WEBSITE_BASE_PATH,
},
};
},
})
],
controllers: [],
providers: [
],
})
export class AppModule implements NestModule {
}
here I am using Nest.js Config, but you don't need to, so use it how you want.
I know that my english is not the best, so if you still do not understand you can check these sources https://docs.nestjs.com/fundamentals/dynamic-modules#configurable-module-builder
https://trilon.io/blog/nestjs-9-is-now-available#Configurable-module-builder

Related

Dynamic ConfigModule cannot Inject in custom JwtModule

I created my Dynamic configModule to extract environment variables from a different path. It extracts from an yml file. Everything works properly if a add in some module. Here is my ConfigModule:
import { DynamicModule } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { EnvConfigService } from './env.config.service';
export class EnvConfigModule {
/**
* Create an static function to call directly from the class without instantiation
* #param options: Our config module attributes or properties
* #returns DynamicModule
*/
static register(options): DynamicModule {
return {
module: ConfigModule,
providers: [
{
provide: 'CONFIG_OPTIONS',
useValue: options,
},
EnvConfigService,
],
exports: [EnvConfigService],
};
}
}
Now when I want to add that configuration in the new custom JwtModule, CustomJwtModule:
...
import { EnvConfigModule } from 'src/utils/environment/env.config.module';
import { EnvConfigService } from 'src/utils/environment/env.config.service';
#Module({
imports: [
JwtModule.registerAsync({
imports: [EnvConfigModule],
inject: [EnvConfigService],
useFactory: (configService: EnvConfigService) => {
const base64_pubKey = configService.get(ACCESS_PUBLIC_KEY_NAME);
return {
publicKey: Buffer.from(base64_pubKey, KEY_ENCODING),
signOptions: {
algorithm: ENCRYPTION_ALGORITHM,
},
};
},
}),
],
providers: [
{
provide: JWT_ACCESS_SERVICE,
useExisting: JwtService,
},
],
exports: [JWT_ACCESS_SERVICE],
})
export class JWTAccessModule {}
And here is my error:
Error: Nest can't resolve dependencies of the JWT_MODULE_OPTIONS (?). Please make sure that the argument EnvConfigService at index [0] is available in the JwtModule context.
I guess, I do get that error because it does not inject properly in JWT module my service.
My app.module.ts is implemented in that way
...
import { EnvConfigModule } from './utils/environment/env.config.module';
#Module({
imports: [
IamModule,
EnvConfigModule.register({
folder: 'config/environment/',
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Ok, it seems I was importing wrongly. It was missing some part of the configuration. Here is the working example of CustomJwtModule:
...
import { EnvConfigModule } from 'src/utils/environment/env.config.module';
import { EnvConfigService } from 'src/utils/environment/env.config.service';
#Module({
imports: [
JwtModule.registerAsync({
imports: [EnvConfigModule.register({
folder: 'config/environment/',
}),
],
inject: [EnvConfigService],
...

Nest can't resolve dependencies of the JwtService though exported from another module

I am using #nestjs/jwt in my NestJS project.
I have two modules, AuthModule and AppModule.
The AuthModule uses the #nestjs/jwt
The AppModule invoke auth service from AuthModule.
AuthService:
import { Injectable } from '#nestjs/common';
import { JwtService } from '#nestjs/jwt';
import { ForbiddenException } from '#nestjs/common';
#Injectable()
export class AuthService {
constructor(private readonly jwt: JwtService) {}
async validate(token: string) {
return this.jwt.verify(token);
}
...
}
AuthModule:
import { Module } from '#nestjs/common'
import { TokenService } from './auth.service'
import { ConfigModule, ConfigService } from '#nestjs/config'
import { JwtModule } from '#nestjs/jwt'
#Module({
imports: [
JwtModule.register({
secret: "mysecret",
signOptions: { expiresIn: "60s" },
}),
],
// I provide AuthService & JwtService
providers: [AuthService, JwtService],
// I export AuthService and JwtService
exports: [AuthService, JwtService],
})
export class AuthModule {}
AppModule:
#Module({
imports: [
AuthModule,
...
],
controllers: [AppController],
// I provide AuthService & JwtService also in AppModule
providers: [AppService, JwtService],
})
export class AppModule {}
(The AppController invokes AuthService instance to validate token.)
I constantly get error:
Nest can't resolve dependencies of the JwtService (?). Please make sure that the argument JWT_MODULE_OPTIONS at index [0]
Why is that? Where do I miss?
You don't need to add JwtService to the providers. Nest will look through the current module's providers, then the imported module's exports, and then the global module's exports to find providers that are not immediately in the level it's resolving the providers in. This means that for AuthService it'll look up to the JwtModule's exports and find the JwtService already created, and use that instance. Then in the AppModule you don't need to mention the AuthService or JwtService, just import the AuthModule that exports the AuthService and JwtModule (doing some module re-exporting) and you should be good to go
EDIT: Added module code:
AuthModule
import { Module } from '#nestjs/common'
import { TokenService } from './auth.service'
import { ConfigModule, ConfigService } from '#nestjs/config'
import { JwtModule } from '#nestjs/jwt'
#Module({
imports: [
JwtModule.register({
secret: "mysecret",
signOptions: { expiresIn: "60s" },
}),
],
providers: [AuthService],
exports: [AuthService, JwtModule],
})
export class AuthModule {}
AppModule
#Module({
imports: [
AuthModule,
...
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

Nest can't resolve dependencies of the xService

error (cont): Please make sure that the argument PrismaService at index [0] is available in the Assoc_Page_TagModule context.
There are no errors in the code.
When I attempt > npm run start:dev
I initially get: Found 0 errors. Watching for file changes.
Then an error:
[ExceptionHandler] Nest can't resolve dependencies of the Assoc_Page_TagService (?). Please make sure that the argument PrismaService at index [0] is available in the Assoc_Page_TagModule context.
What is the argument at index [0]? Why is it expecting an argument?
Assoc_Page_Tag.service.ts
import { Injectable } from '#nestjs/common';
import { PrismaService } from '../database';
import { Prisma } from '#prisma/client';
#Injectable()
export class Assoc_Page_TagService {
constructor(private readonly prisma: PrismaService) {}
Assoc_Page_TagsAll() {
return Promise.all([
this.prisma.assoc_Page_Tag.findMany()]).then(([records, total]) => {
return {
records,
metadata: { total },
};
});
}
Assoc_Page_Tag.module.ts
import { Module } from '#nestjs/common';
import { Assoc_Page_TagController } from './Assoc_Page_Tag.controller';
import { Assoc_Page_TagService } from './Assoc_Page_Tag.service';
#Module({
imports: [],
controllers: [Assoc_Page_TagController],
providers: [Assoc_Page_TagService],
exports: [Assoc_Page_TagService],
})
export class Assoc_Page_TagModule {}
Assoc_Page_Tag.controller.ts
import {
Body,Controller,Get,Param,ParseIntPipe,Put,
} from '#nestjs/common';
import { Assoc_Page_TagService } from './Assoc_Page_Tag.service';
import { RESOURCE_BASE_ROUTE } from '../constant';
import { Prisma } from '#prisma/client';
const Route = RESOURCE_BASE_ROUTE.assoc_Page_Tag
#Controller()
export class Assoc_Page_TagController {
constructor(private readonly assoc_page_tagService: Assoc_Page_TagService) {}
#Get(`${Route}`)
all() {
return this.assoc_page_tagService.Assoc_Page_TagsAll();
}
}
app.module.ts
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { Assoc_Page_TagModule } from './Assoc_Page_Tag';
#Module({
imports: [
Assoc_Page_TagModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Missing reference to PrismaModule - which exports PrismaService
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PrismaModule } from './database';
import { Assoc_Page_TagModule } from './Assoc_Page_Tag';
#Module({
imports: [
PrismaModule,
Assoc_Page_TagModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

Can't figure out nest error after adding MongooseModule to AuthModule

auth.module.ts
import { Module, forwardRef } from '#nestjs/common';
import { AuthService } from './auth.service';
import { UserService } from '../user/user.service';
import { PassportModule } from '#nestjs/passport';
import { LocalStrategy } from './local.strategy';
import { JwtModule, JwtService } from '#nestjs/jwt';
import { MongooseModule } from '#nestjs/mongoose';
import { ConfigModule } from '#nestjs/config';
import { JwtStrategy } from './jwt.strategy';
import { UserModule } from 'src/user/user.module';
import { User, UserSchema } from '../user/schemas/user.schema';
#Module({
providers: [AuthService, LocalStrategy, UserService, JwtService, JwtStrategy],
imports: [
ConfigModule.forRoot({ isGlobal: true }),
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
PassportModule,
JwtModule.register({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '1800s' },
}),
forwardRef(() => UserModule),
],
exports: [AuthService, JwtService],
})
export class AuthModule {}
user.module.ts
import { Module, forwardRef } from '#nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { AuthModule } from '../auth/auth.module';
import { AuthService } from '../auth/auth.service';
import { MongooseModule } from '#nestjs/mongoose';
import { User, UserSchema } from './schemas/user.schema';
#Module({
imports: [
forwardRef(() => AuthModule),
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
],
providers: [UserService, AuthService],
controllers: [UserController],
exports: [UserService],
})
export class UserModule {}
error message
Nest can't resolve dependencies of the JwtService (?). Please make sure that the argument JWT_MODULE_OPTIONS at index [0] is available in the AuthModule context.
I'm importing Jwt from nest. Once Mongoose is added to auth.module I get endless errors when I try and update anything. I had a hard coded array to test out routes before and it worked just fine.
You don't need to re-add provides to the providers array if they are provided from another module. Doing this will make Nest instantiate a new instance of the provider (breaking the singleton scoping), and will require you to have all of the configuration necessary for the provider (in this case, the JWT_MODULE_OPTIONS

NestJS can't resolve a service dependency

I am trying to inject a ContentsService into a SlackModule so that I can use some of the Content functions in my Slack Controller.
This is app.module
#Module({
imports: [
ContentsModule,
ZapierModule,
SlackModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Here is my Contents Module:
import { Module } from '#nestjs/common';
import { MongooseModule } from '#nestjs/mongoose';
import { ContentsController, ZapierController, SlackInteractiveController } from './contents.controller';
import { ContentsService } from './contents.service';
import { ContentSchema } from './content.model';
#Module({
imports: [
MongooseModule.forFeature([{ name: 'Content', schema: ContentSchema }]),
],
controllers: [ContentsController, ZapierController, SlackInteractiveController],
providers: [ContentsService],
exports: [ContentsService],
})
export class ContentsModule {}
This is my Slack Module:
import { Module } from '#nestjs/common';
import { SlackService } from './slack.service';
import { SlackController } from './slack.controller';
import { ContentsService } from '../contents/contents.service';
#Module({
providers: [SlackService],
controllers: [SlackController],
imports: [ContentsService],
})
export class SlackModule {}
And my Slack Controller
import { ContentsService } from '../contents/contents.service'
#Controller('slackevents')
export class SlackController {
constructor(private contentsService: ContentsService) {}
But when I run this code, I get this error:
[Nest] 75628 - 05/22/2020, 7:08 AM [ExceptionHandler] Nest can't resolve dependencies of the ContentsService (?). Please make sure that the argument at index [0] is available in the ContentsService context. +44ms\
What is it that I am doing wrong?
Services and other providers do not belong in the imports array. Only module classes should go in there. You should have imports: [ContentsModule] in your SlackModule and you'll have access to the ContentsService

Resources