How to export APP_GUARD from nestjs module? - nestjs

I have module which implement JWT passport
const appGuard = {
provide: APP_GUARD,
useClass: JwtAuthGuard,
};
#Module({})
export class JwtAuthModule {
static forRoot(options: { jwks: string, excludePath: string[] }): DynamicModule {
const exportableProviders = [
{ provide: JWKS_URI, useValue: options.jwks },
{ provide: EXCLUDE_FROM_AUTH, useValue: options.excludePath },
appGuard,
];
return {
module: JwtAuthModule,
providers: [
...exportableProviders,
JwtStrategy,
JwtAuthGuard
],
exports: exportableProviders,
};
}
}
But I am getting an error:
Error: Nest cannot export a provider/module that is not a part of the
currently processed module (JwtAuthModule). Please verify whether the
exported APP_GUARD is available in this particular context.
Possible Solutions:
Is APP_GUARD part of the relevant providers/imports within JwtAuthModule?
What is interesting if I debug i see that APP_GUARD has name in providers like: APP_GUARD (UUID: 63cf5b8e-815e-4034-a4a8-c6196d98b612)
Simple work around is to register APP_GUARD in providers at each root module and import module, but i would love to be able just to import this module once if possible...

APP_GUARD, along with the other APP_* providers, are special provider tokens that Nest allows to be bound multiple times. As soon as it is used it is bound as a global enhancer of whatever type relating back to the token (pipe, guard, interceptor, filter). As it's bound, there's no need to export the enhancer as well, there's nothing else that can make use of it directly, it's bound globally so it's already fine as is. Just remove the APP_GUARD provider you have from the exports and you should be good to go

Related

NestJS dynamic module registration

I have a question regarding dynamic modules importing.
Let's say I have #Injectable service which depends on the parameter that came from the configuration:
#Injectable()
export class StatsService {
constructor(#Inject(SERVICE_NAME) private serviceName: string) {}
...
}
Let's say I expose this service through the Dynamic module:
#Module({})
export class CoreServicesModule {
static register(coreConfig: CoreServicesConfig): DynamicModule {
const { serviceName } = coreConfig;
return {
module: CoreServicesModule,
providers: [
{
provide: SERVICE_NAME,
useValue: serviceName
},
StatsService
],
exports: [
StatsService
]
};
}
}
Let's say my application is pretty big, and I have a lot of different modules.
But I need StatsService in every one of them.
So for example to be able to use StatsService in one of the modules i need to do something like:
#Module({
imports: [CoreServicesModule.register({ serviceName: 'test', ... })]
})
export class SomeModule { ... }
And I need to do it for each module...
Do we have a way to do it only once in NestJS?
Or how we can re-use the already registered module?
#Global decorator also not help me here cause I still need to import the module in each place...
#Global() decorator will help you if you are interested in using StatService in all other modules without the need to import that module!
BUT from your code it seems that each module that import CoreServicesModule would also make a small adjustment to it by dynamically specifying the providers it has (such as ValidationPipe).
in that case you should NOT make it #Global() since global modules should be registered only once

NestJS inject custom TypeOrm repository based on an Interface

I'm currently working through the database integration docs for NestJS using TypeOrm. In these docs there are examples that show how to inject a custom database repository using the app.module from NestJS. All of these examples inject classes using the actual type of the custom repository.
#Injectable()
export class AuthorService {
constructor(private authorRepository: AuthorRepository) {}
}
This code is injected via the app.modules by providing a import like such:
#Module({
imports: [TypeOrmModule.forFeature([AuthorRepository])],
controller: [AuthorController],
providers: [AuthorService],
})
export class AuthorModule {}
This works well if you are fine with programming against an implementation, but I prefer to use an interface in my classes. I've already found the solution to injecting classes via an interface with NestJS in a previous question, but when I try to inject my custom repository like that, it doesn't seem to instanciate correctly and becomes undefined.
(node:16658) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'save' of undefined
Because of this, I assume you can only inject customRepositories via the forFeature() call in the app.module, but that won't allow me to use interfaces for injection, as far as I know. Is there any other way I can inject a custom TypeOrm repository without having the replace all my interfaces for the implementation of my custom repository? Thanks in advance!
Edit
Here is my current code, I managed to get it to inject, but this still forces me to use the implementation instead of the interface each time I call the constructor. This is mainly an issue when testing due to mocking.
#CommandHandler(FooCommand)
export class FooHandler
implements ICommandHandler<FooCommand> {
private fooRepository: IFooRepository; // Using Interface as a private property.
private barEventBus: IEventBus;
constructor(fooRepository: FooRepository,
barEventBus: EventBus) { // Forced to use implementation in constructor for injection.
this.fooRepository = fooRepository;
this.barEventBus = barEventBus;
}
#EntityRepository(FooEntity)
export class FooRepository extends Repository<FooEntity> implements IFooRepository {
getFoo() {
// Do stuff
}
}
#Module({
imports: [TypeOrmModule.forRoot(), TypeOrmModule.forFeature([FooRepository]],
// Other module setup
})
export class AppModule {}
It should work with using the InjectRepository decorator where you specify the Repository but then you type is as your interface instead and when testing you just provide the IFooRepository!
Example code:
constructor(#InjectRepository(FooRepository) fooRepository: IFooRepository,
barEventBus: EventBus) {
Edit: This answer is crap, that abstract-class-as-interface hack used does not work out as the defined methods seem to be optional to implement despite being marked as abstract.
Well, kind of got it working. Based on this answer https://stackoverflow.com/a/74561702/913136 I used an abstract class as interface (you can actually implement it) for not being required to pass strings around as tokens. Only drawback is the misuse of the abstract class. Not sure yet if I like it.
Using an actual interface in the same way seems not to be possible unfortunately. Urgh.
#Module({
imports: [
TypeOrmModule.forRoot({
...dataSource.options,
autoLoadEntities: true,
}),
TypeOrmModule.forFeature([Listing]),
],
controllers: [ViewListingController],
providers: [
{
provide: ListingRepository,
useClass: TypeOrmListingRepository,
},
],
})
makeshift interface:
import { Listing } from "./Listing";
export abstract class ListingRepository {
abstract findMostRecent: () => Promise<Listing[]>;
}
implementation:
import { Listing, ListingRepository } from "../../Domain";
import { Injectable } from "#nestjs/common";
import { Repository, DataSource } from "typeorm";
#Injectable()
export class TypeOrmListingRepository
extends Repository<Listing>
implements ListingRepository
{
constructor(private dataSource: DataSource) {
super(Listing, dataSource.createEntityManager());
}
findMostRecent() {
return this.find({});
}
}
import { Controller, Get } from "#nestjs/common";
import { ListingRepository } from "../Domain";
#Controller("listings")
export class ViewListingController {
constructor(private readonly listingRepo: ListingRepository) {}
#Get("/most-recent")
listMostRecent() {
return this.listingRepo.findMostRecent();
}
}

Nestjs can't resolve dependencies of XModel

I have been working on this app for like 3 months which is near completion. But since yesterday I have been unable to solve this problem. I want to use activityTypeService in activityLogService and I have been getting this wired error. I have already exported activityTypeservice in its module. see below
below is ActivityTypeModule, I export ActivityTypeService so it can available in ActivityLogService
#Module({
imports: [MongooseModule.forFeature([{ name: 'ActivityType', schema: ActivityTypeSchema },])],
providers: [ActivityTypeService,],
controllers: [ActivityTypeController],
exports: [ActivityTypeService,]
})
export class ActivityTypeModule { }
The code below is activityLog module and ActivityTypeModule is imported
#Module({
imports: [MongooseModule.forFeature([{ name: 'ActivityLog', schema: ActivityLogSchema }]), ActivityTypeModule],
providers: [ActivityLogService, ActivityTypeModule],
controllers: [ActivityLogController],
exports: [MongooseModule,]
})
export class ActivityLogModule { }
so I use it the activityLogService as shown below
#Injectable()
export class ActivityLogService {
constructor(#InjectModel('ActivityLog') private activitylogModel: Model<ActivityLog>,
private activityTypeService: ActivityTypeService
) { }
async activitylog(activityuser: string, activitytype: string, activitydetails: string, activitydate: Date) {
const activitiesLog = {
activityuser: activityuser,
activitytype: activitytype,
activitydetails: activitydetails,
activitydate: activitydate
}
const activity = new this.activitylogModel(activitiesLog);
console.log(activity);
await activity.save()
}
}
But I am still getting this wired error which I dont understand
Nest can't resolve dependencies of the ActivityTypeService (?). Please make sure that the argument ActivityTypeModel at index [0] is available in the ActivityTypeService context.
Potential solutions:
- If ActivityTypeModel is a provider, is it part of the current ActivityTypeService?
- If ActivityTypeModel is exported from a separate #Module, is that module imported within ActivityTypeService?
#Module({
imports: [ /* the Module containing ActivityTypeModel */ ]
})
From your error, somewhere you have ActivityTypeService in an imports array somewhere, which shouldn't be happening. Along with that, I can see you have ActivityTypeModule in a providers array, which is also not supported, though Typescript doesn't have a good way to show errors about that, so you aren't seeing any errors.
In general, as Nest error like that is in the form of
Nest can't resolve dependencies of the <Provider> (?). Please make sure that the argument <injected_valued> at index [<index>] is available in the <module> context.
<Provider> and <module> should never be the same value, and if they are, it's a good indication that you have a provider in an imports array.

Using imported interceptor on controller not working in nestjs

I like to organise my project along feature lines with different modules for cross cutting concerns eg: configuration, authentication, etc. However when importing an Interceptor into a feature module for use with a Controller Nest doesn't seem to reuse the existing instance.
controllers.module.ts
#Module({
imports: [
// ConfigService is exported from this module.
ConfigModule
],
providers: [
// For debugging purposes I used a factory so I could place a breakpoint and see
// when the Interceptor is being created.
{
provide: MyInterceptor,
useFactory: (config: ConfigService) => new MyInterceptor(config),
inject: [
ConfigService
]
}
],
exports: [
MyInterceptor
]
})
export class ControllersModule {
}
customer.module.ts
#Module({
imports: [
ControllersModule
],
controllers: [
CustomerController
],
providers: [
CustomerService
]
})
export class CustomerModule {
}
customer.controller.ts
#Controller("/customers")
#UseInterceptors(MyInterceptor)
export class CustomerController {
constructor(private readonly customerService: CustomerService) {
}
#Get()
customers() {
return this.customerService.findAll();
}
}
When the application starts up, I can see the MyInterceptor provider factory being called, with an instance of ConfigService. However then I see the following error on the console
error: [ExceptionHandler] Nest can't resolve dependencies of the MyInterceptor (?). Please make sure that the argument ConfigService at index [0] is available in the CustomerModule context.
Potential solutions:
- If ConfigService is a provider, is it part of the current CustomerModule?
- If ConfigService is exported from a separate #Module, is that module imported within CustomerModule?
#Module({
imports: [ /* the Module containing ConfigService */ ]
})
Now maybe there's something about how Nest instantiates/uses Interceptors that I'm not understanding but I thought that given that MyInteceptor had been created, and the ControllersModule imported by CustomerModule that the bean would have been available and applied to CustomerController.
Is there something I'm missing here?
Interceptors (along with other request lifecycle classes) are kinda like pseudoproviders in that that are #Injectable() but they aren't added to a providers array for binding. You can bind then via the providers array, (APP_INTERCEPTOR) but that will cause it to be bound globally.
Because interceptors can't be added to a providers array the way you are trying, you need to instead add the ConfigModule to whatever module uses the interceptor.
and as a side-note, you shouldn't use #Res with interceptors in nestjs
why
look, when you are using an interceptor, you are handling (with using .handle()) the stream of response (observable) not a whole package of it, but using express #Res actually is somehow getting around the whole flow of response streaming.
this is also explicitly mentioned in nestjs official documents:
We already know that handle() returns an Observable. The stream
contains the value returned from the route handler, and thus we can
easily mutate it using RxJS's map() operator.
WARNING
The response mapping feature doesn't work with the
library-specific response strategy (using the #Res() object directly
is forbidden).

Inject service into guard in Nest.JS

I have KeysModule, which can be used to add or remove API keys. I need these keys to protect some routes from unauthorized access.
To protect these routes I have created ApiGuard:
import { CanActivate, ExecutionContext, Injectable } from '#nestjs/common';
#Injectable()
export class ApiGuard implements CanActivate {
async canActivate(
context: ExecutionContext,
): Promise<boolean> {
const request = context.switchToHttp().getRequest();
return request.headers.api_key;
}
}
And then I use it in route:
#Get('/protected')
#UseGuards(ApiGuard)
async protected(#Headers() headers: Api) {
const key = await this.ks.findKey({ key: headers.api_key });
if (!key || !key.active) return 'Invalid Key';
return 'Your API key works';
}
Where ks is KeyService used to check if key is correct or not.
This solution works, but is stupid. I have to copy and paste some lines of code everywhere I want to use this guard (I mean lines in route).
I have tried to to move all logic to ApiGuard, but there I have got error, that KeyService cannot be injected to ApiGuard class. To explain, I have KeyService in providers in KeysModule, but ApiGuard is globally used.
Do you have any idea how to do it?
As of NestJS v8 it seems injecting the service as answered by zsoca in the accepted answer doesn't work anymore.
The working solution for NestJS 8 is by providing a class reference instead of a string:
constructor(#Inject(KeyService) private keyService: KeyService) {}
To inject service in guard.You can create a global module.
// ApiModule
import {Module,Global} from '#nestjs/common';
import {KeyService} from '../';
#Global()
#Module({
providers: [ KeyService ],
exports: [KeyService]
})
export class ApiModule {}
Then inject service into guard like this
// guard
export class ApiGuard implements CanActivate {
constructor(#Inject('KeyService') private readonly KeyService) {}
}
async canActivate(context: ExecutionContext) {
// your code
throw new ForbiddenException();
}
Now the problem can be solved.But I have another problem.I want to inject something into service but got this error:
Nest can't resolve dependencies of the AuthGuard (?, +). Please make sure that the argument at index [0] is available in the current context.
And here is my solution:
To Inject other dependency in KeyService,like nestjs docs say.
global guards registered from outside of any module (with useGlobalGuards() as in the example above) cannot inject dependencies since this is done outside the context of any module.
This is their sample:
// app.module.js
import { Module } from '#nestjs/common';
import { APP_GUARD } from '#nestjs/core';
#Module({
providers: [
{
provide: APP_GUARD,
useClass: RolesGuard,
},
],
})
export class ApplicationModule {}
It worked.Now I can use guard global without dependency error.
Maybe it's too late, but I ran the same issue and find a solution. Maybe there is a better one, but it's working properly for me:
Define KeysModule as a global module, you can check how to do it in nestjs docs: https://docs.nestjs.com/modules
After You can do this:
import { CanActivate, ExecutionContext, Injectable } from '#nestjs/common';
#Injectable()
export class ApiGuard implements CanActivate {
constructor(
#Inject('KeyService')
private readonly ks
) {}
const key = await this.ks.findKey();
"YOUR_CODE_HERE..."
}
Hope it's help to you or somebody who will stuck with this in the future.
You can inject a service in a guard like in any object annotated with Injectable.
If your ApiGuard need KeyService you have two choices:
Add ApiGuard in a module which import KeysModule. Then import the created module to use ApiGuard globally
Add ApiGuard in KeysModule and export it.
Services used in the guard must be exported in its module. Providing service is not enough!
Let me clarify some points here! Nestjs Guard layer was designed to let you interpose processing logic at exactly the right point in the request/response cycle and this was treated as a normal instance in Nestjs DI Container.
So what does it means when I said this was treated as a normal instance in Nestjs DI Container -> So whenever it has dependencies so that wherever it is used you need to also inject it own dependencies to where you are using. Let me give you some example:
You first have a module corresponding to handle tokens as you mentioned ^^.
#Module({
imports: [CacheModule.register()],
providers: [
TokenService,
],
exports: [TokenService],
})
export class TokenModule {}
And a Guard use it to do something ...
class YourCustomAuthGuard implements CanActive {
constructor(private readonly tokenService: TokenService) {}
canActive(executionCtx: ExecutionContext) {
this.tokenService.getSth() // ...
}
}
And you have some modules like Post or Category and there are some routing needs to verify your token existed by using guard.
#Controller('posts')
class PostController {
#UseGuards(YourCustomAuthGuard)
#Get()
getSelfPosts() {}
}
#Controller('categories')
class CategoryController {
#UseGuards(YourCustomAuthGuard)
#Get()
getPermittedCategories() {}
}
The thing here you want to use YourCustomAuthGuard as a special dependency in Post and Category module which we don't need to declare in Module as Nestjs do understand and treat Guard as a special dependency for handling execution context. So the case here is your Guard depended on TokenModule then wherever you use your Guard you need to inject TokenModule to follow with YourCustomGuard. Below is the example of PostModule and CategoryModule:
#Module({
imports: [TokenModule, TypeOrmModule.feature([Post])], // <-- Take a look at TokeModule that I have been declared
controllers: [PostController],
providers: [PostService],
exports: [PostService],
})
class PostModule {}
#Module({
imports: [TokenModule, TypeOrmModule.feature([Category], // <-- Take a look at TokeModule that I have been declared
controllers: [CategoryController],
providers: [CategoryService],
exports: [CategoryService],
})
class CategoryModule {}
So that this way can resolve your problem as your guard is used independently and does not belong to the global scope.
So what if you need to use it in your global scope? In my opinion there are two ways:
Firstly, your dependencies also belong to a global module. Let me give out some examples:
// This is the root module
#Module({
imports: [TokenModule], // <- TokenModule you declared as a global module
providers: [
{
provide: APP_GUARD,
useClass: YourCustomAuthGuard,
},
]
})
class AppModule {}
Second way is to construct directly when declaring a global guard (This way is not recommended)
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
{
logger: true,
});
const tokenService = app.get(TokenService);
app.useGlobalGuards(new YourCustomAuthGuard(tokenService));
After all, we may have some ways to resolve dependencies in nestjs guard but there is one thing that belongs to passport and nestjs design I want to present here because it may help you to have a better view to loosely coupling between nest guard and validation strategy separately and we no need to inject dependencies anywhere else. Before continuing reading the below topic, I recommend viewers that read and try with nestjs authentication before!
There are maybe some devs may used and read about nestjs authentication before and maybe also curious about how PassportStrategy and PassportGuard interact with each other and also we can easily inject things into PassportStrategy, right?
I won't go deep down into passport and nestjs implementation but I will explain about the concept then you guys can have an overview about this one.
Firstly, keep in my that guard layer still be triggered as normal if we use it in protecting any route and then we go next to the triggering strategy! But wait, we don't see any relationship between our Guard and Strategy so how can it work? I will give you an example of how I implemented a module using passport!
#Module({
imports: [
UserModule,
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
secret: configService.get<string>('JWT_SECRET'),
}),
inject: [ConfigService],
}),
],
controllers: [AuthController],
providers: [
JwtStrategy, // <- We provide JwtStrategy as a provider here
{
provide: AuthServiceToken,
useClass: AuthServiceImpl,
},
],
})
export class AuthModule {}
Be aware that JwtStrategy extends from PassportStrategy and our guard also extends from AuthGuard of nestjs/passport.
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(configService: ConfigService) {
const secret = configService.get<string>('JWT_SECRET');
assert(secret, 'JWT_SECRET is missing');
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: secret,
});
}
validate(payload: JwtPayload): JwtPayload {
return payload;
}
}
Actually, behind scenes, passport is a separate instance out of the box of nestjs DI Container so the author of nestjs kamil created #nestjs/passport as an adapter to this library.
Firstly, let's focus on our JwtAuthGuard, we extend from AuthGuard('jwt') which is a guard factory actually and it use 'jwt' to identify strategy to be used next so that after finishing guard validation, it can make a round trip to strategy in next section under the hood.
And next, pay attention on JwtStrategy, this is where the cool part happen, this one would be initialized as a normal provider in the nestjs container so that everything can easily inject to this one and after nest injected dependencies into this one, nestjs will pass this instance reference to Passport so that can be auto triggering under the hood after AuthGuard finish their job at runtime! Read more about nestjs passport strategy and focus on passportInstance.use and spend time you would have a nice idea to use stragy out of the box with Guard in nestjs. Good luck ^^
Another approach without making the service global (so no #Global, and no #Inject) worked for me (on Nest 9)
File Structure
├── app
│ └── app.module.ts # main application module
├── status
│ ├── status.module.ts
│ └── status.service.ts # status service (which is to be injected)
├── hodor
│ ├── hodor.decorators.ts # avoid using #UseGuards(HodorGuard) => use #NotDangerousNow() instead
│ └── hodor.guard.ts # the guard using StatusService
└── door
├── door.module.ts
└── door.controller.ts # the controller guarded by #NoDanger()
AppModule
import { Module } from '#nestjs/common'
import { APP_GUARD } from '#nestjs/core'
import { DoorModule } from '../door/door.module'
import { HodorGuard } from '../hodor/hodor.guard'
import { StatusModule } from '../status/status.module'
#Module({
imports: [
HodorModule,
DoorModule,
StatusModule
],
providers: [
{ provide: APP_GUARD, useClass: HodorGuard } // makes the guard used everywhere
]
})
export class AppModule {}
StatusModule
import { Module } from '#nestjs/common'
import { StatusService } from './status.service'
#Module({
providers: [StatusService],
exports: [StatusService]
})
export class StatusModule {}
StatusService
import { Injectable } from '#nestjs/common'
#Injectable()
export class StatusService {
/**
* Returns whether Hodor thinks it's dangerous at the given time.
* Just async because in general there is a db call there.
*/
async isDangerousAt(date: Date): Promise<boolean> {
return date.getHours() > 22
}
}
hodor.decorators.ts
import { SetMetadata } from '#nestjs/common'
export const DANGER_KEY = 'danger'
// 403 if Hodor thinks it's dangerous at the given date/time
// usage: #NotDangerous(date) => that's right I see no possible usage for that one, it's for the example
export const NotDangerous = (date: Date) => SetMetadata(DANGER_KEY, { date })
// 403 if Hodor thinks it's dangerous right now
// usage: #NotDangerousNow()
export const NotDangerousNow = () => SetMetadata(DANGER_KEY, { date: new Date() })
hodor.guard.ts
import { Injectable, CanActivate, ForbiddenException } from '#nestjs/common'
import { Reflector } from '#nestjs/core'
import { DANGER_KEY } from './hodor.decorator'
import { StatusService } from '../status/status.service'
type HodorMetadata = {
status: PlatformHodor
expected: boolean
}
#Injectable()
export class HodorGuard implements CanActivate {
constructor(
private reflector: Reflector,
private readonly statusService: StatusService // Do not use #Inject (or nest won't be able to inject it)
) {}
/**
* Rely on status service to check if Hodor thinks it is dangerous at the given date/time.
* #throws ForbiddenException if Hodor thinks it's dangerous at the given date/time => 403
*/
async canActivate(context: any): Promise<boolean> {
// METADATA DANGER_KEY is the magic link between NotDangerous decorator and the guard
const metadata = this.reflector.getAllAndOverride<HodorMetadata>(
DANGER_KEY,
[context.getHandler(), context.getClass()]
)
// because we inject the guard in the whole app
// => it must let pass in routes with no decorator
if (!metadata) return true
// 403 if dangerous
const isDangerous = await this.statusService.isDangerousAt(metadata.date)
if (isDangerous) {
throw new ForbiddenException(`Hodor thinks it's dangerous on ${metadata.date}`)
}
// let pass otherwise
return true
}
}
DoorModule
import { Module } from '#nestjs/common'
import { DoorController } from './door.controller'
#Module({
controllers: [DoorController],
providers: [DoorService],
})
export class DoorModule {}
DoorService
import { Controller, HttpCode, Post } from '#nestjs/common'
import { NotDangerousNow } from '../hodor/hodor.decorator'
#Controller('door')
export class DoorController {
#NotDangerousNow()
#Post()
open(): Promise<string> {
return 'please, come in'
}
}

Resources