I'm currently struggling with service injection in a guard.
Well, the thing is I made a JwtAuthGuard, that needs both my FirebaseService and my UserService.
So I thought it'd be useful to create a AuthGuardModule so i only need to import my AuthGuardModules in the modules that have a Controller, to use my Guard.
The thing is that unfortunately, i need this guard for my UserModule too.
So that's natural that i get a Circular Dependency Error When i import my AuthGuardModule in my UserModule.
Then, i tried all the things more or less related to Circular Dependency Error in Nest.js, but no can do.
Can someone tell me if:
Circular Dependency Error ?@Injectable()
export class JwtAuthGuard implements CanActivate {
private readonly logger = new Logger(JwtAuthGuard.name)
constructor(
private readonly firebaseService: FirebaseService,
private readonly userService: UserService
) { }
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = request.headers['authorization'];
if (!token) {
throw new UnauthorizedException
}
try {
const firebaseUser = await this.firebaseService.getUserByToken(token)
request.user = await this.userService.getByEmail(firebaseUser.email)
return true
} catch (error) {
this.logger.error(this.canActivate.name, error)
throw new UnauthorizedException
}
}
}
@Module({
imports: [UserModule],
providers: [FirebaseService, JwtAuthGuard],
exports: [JwtAuthGuard]
})
export class AuthGuardModule {}
Well, jmcdo29 of the NestJS discord explained me that:
Your
AuthGuardModuleshould export theUserModuleand theFirebaseService. Guards, when used in the@UseGuards()decorator, are used in the context of the current controller's module, instead of being looked at as anInjectableprovider
So I did that way and it did solve my initial issue, but i have another circular dependency issue.
First, let's resume resume the architecture i have:
UserModule to create a user into my db when i call the register route@Module({
imports: [
AuthGuardModule
],
controllers: [AuthController],
providers: [AuthService, EmailService, FirebaseService],
})
export class AuthModule {}
@Module({
imports: [
forwardRef(() => AuthGuardModule),
BlobModule,
MongooseModule.forFeature([{ name: File.name, schema: FileSchema }])
],
controllers: [FileController],
providers: [FileService],
exports: [FileService],
})
export class FileModule {}
FileModule, to check if the avatar exists when it's modified (i have only one route that updates all my users personal data)AuthGuardModule to use my JwtAuthGuard@Module({
imports: [
forwardRef(() => AuthGuardModule),
forwardRef(() => FileModule),
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])
],
controllers: [UserController],
providers: [UserService, FirebaseService],
exports: [UserService, FileModule]
})
export class UserModule {}
UserModule to get the user in my db@Module({
imports: [forwardRef(() => UserModule), FirebaseModule],
providers: [JwtAuthGuard, RegisterAuthGuard],
exports: [JwtAuthGuard, RegisterAuthGuard, FirebaseModule, UserModule]
})
export class AuthGuardModule {}
I could use firebase to store my users avatars and do it front side, but i have to store it in Azure Blob Storage (it's imposed to my), so the FileModule handle all that thing
So, now, that i solved my AuthGuardsModule -> UserModule circular dependecy thanks to you guys, i have a new circular dependency issue: AuthGuardsModule -> UserModule -> FileModule
I set a forwardRef of the AuthGuardsModule inside the FileModule but nest naturally said me that make sure that each side of a bidirectional relationships are decorated with "forwardRef()"
So i put a forwardRef to my FileModule import inside my UserModule, but same thing.
Any idea ?
From my perspective, you would need to make use of the forwardRef method from the @nestjs/common package, which is in charge of handling circular dependency when it occurs. See Circular dependency - NestJS docs
One thing to take into account is the location of your JwtAuthGuard. Was it initially inside the AuthModule or within another shared module ?
I will assume the guard was initially in your AuthModule (for example let's say /src/auth/guards/auth-guard.ts).
I also assume that the FirebaseService lies within a FirebaseModule and is being exported from there, and that the FirebaseModule injectables don't need other classes that depends on other circular referencing classes/modules. (In this case, you would need to indicate Nest how it should handle it by adding more forwardRefs within the involved classes/modules)
Based on the code you provided, you could do something like this:
@Module({
imports: [forwardRef(() => UserModule), FirebaseModule],
providers: [...yourProviders],
exports: [...yourExportedInjectables]
})
export class AuthModule {}
@Module({
imports: [...requiredImports],
providers: [FirebaseService],
exports: [FirebaseService]
})
export class FirebaseModule {}
@Module({
imports: [forwardRef(() => AuthModule)],
providers: [...yourProviders],
exports: [...yourExportedInjectables]
})
export class UserModule {}
NB: You could also use forwardRef at the service layer depending on your specific use cases.
NB2: Guards could be grouped within a shared module that could be imported in the AuthModule, so that you don't have to specify Nest to forward refs between every modules, but only reference AuthModule in the modules using the given guard(s).
Hope it helps !
Again, I don't have much context on your architecture, so I provided code sample based on assumptions.
Don't hesitate to comment / provide more context so I can edit my answer accordingly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With