How to inject service inside a guard Nest js - dependency-injection

I have created a guard for global use. Whenever I am trying to inject my user service into the guard getting this error while compiling - Nest can't resolve dependencies of the VerifyUserGuard (?). Please make sure that the argument dependency at index [0] is available in the UserModule context.
My user service is a part of user module and inside the user service I have used #InjectRepository(User) private readonly _usersRepository: Repository<User>
Guard -
import { CanActivate, ExecutionContext, Injectable } from '#nestjs/common';
import { UserService } from '#v1/user';
import { VerifyUserDto } from '#v1/user/dto';
#Injectable()
export class VerifyUserGuard implements CanActivate {
//getting error because of this
constructor(private _userService: UserService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest<Request>();
const requestBody = request.body as unknown as VerifyUserDto;
console.log(requestBody);
const user = await this._userService.findOneByEmail(requestBody.email);
return true;
}
}
User module -
import { MailModule } from '#mail';
import { Module } from '#nestjs/common';
import { UserService } from './user.service';
import { User } from './entities/user.entity';
import { TypeOrmModule } from '#nestjs/typeorm';
import { UserController } from './user.controller';
#Module({
imports: [MailModule, TypeOrmModule.forFeature([User])],
controllers: [UserController],
providers: [UserService],
exports: [UserService],
})
export class UserModule {}

I believe you need
either to inject your module or service (with providing if not module) in AppModule if you connect your guard
or use #Global() decorator for your module with service, e.g.
#Global()
#Module({
imports: [MailModule, TypeOrmModule.forFeature([User])],
controllers: [UserController],
providers: [UserService],
exports: [UserService],
})
export class UserModule {}

Try this It worked for me. Use #Inject Decorator.
constructor(#Inject(AppService) private _userService: UserService) {}

Related

Can we export custom Guards from nestjs dynamic module and use it inside host module?

email-verification.guard.ts
#Injectable()
export class EmailVerificationGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const skipEmailVerification = this.reflector.get<boolean>('skipEmailVerification', context.getHandler());
if (skipEmailVerification) {
return true;
}
const request: Request = context.getArgs()[0];
if (!request.authPayload) {
throw new ForbiddenException('User not found');
}
if (!request.authPayload[Auth0Namespace.AppMetadata]) {
throw new ForbiddenException('Please verify your email before continuing');
}
return true;
}
}
dynamic-auth.module.ts
import { DynamicModule, Module } from '#nestjs/common';
import { authService } from './auth.service';
import { EmailVerificationGuard } from './email-verification.guard';
#Module({})
export class DynamicAuthModule {
static register(): DynamicModule {
return {
module: DynamicAuthModule,
providers: [authService, EmailVerificationGuard],
exports: [authService, EmailVerificationGuard],
};
}
}
app module (host)
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DynamicAuthModule, EmailVerificationGuard } from 'dyamic-auth-module';
#Module({
imports: [DynamicAuthModule.register({ folder: './config' })],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Can I use EmailVerificationGuard in this host module? If not why?
Note:
nestjs packages version: 8.4.7
I tried this but I get this reflector dependency issue, How to resolve this?
Error: Nest can't resolve dependencies of the EmailVerificationGuard (?). Please make sure that the argument Reflector at index [0] is available in the AppModule context.
Potential solutions:
- If Reflector is a provider, is it part of the current AppModule?
- If Reflector is exported from a separate #Module, is that module imported within AppModule?
#Module({
imports: [ /* the Module containing Reflector */ ]
})
Is there any other way to handle this?

Implementing strategy in nest.js

I am trying to use the strategy pattern for the service, however the Module I try to use as context for strategy seems to only stick to one of the two. Here is the example code:
animal.module.ts
#Module({})
export class AnimalModule {
static register(strategy): DynamicModule {
return {
module: AnimalModule,
providers: [{ provide: 'STRATEGY', useValue: strategy }, AnimalService],
imports: [],
exports: [AnimalService]
};
}
}
animal.service.ts
#Injectable()
export class AnimalService {
constructor (#Inject('STRATEGY') private strategy) {
this.strategy = strategy
}
public makeSound() {
return this.strategy.makeSound()
}
}
cat.module.ts
#Module({
imports: [
AnimalModule.register(catStrategy),
],
controllers: [CatController],
providers: [CatService],
})
export class CatModule {}
cat.service.ts
#Injectable()
export class CatService {
constructor(
private readonly animalService: AnimalService,
) {}
public makeSound() {
return this.animalService.makeSound()
}
}
dog.module.ts
#Module({
imports: [
AnimalModule.register(dogStrategy),
],
controllers: [DogController],
providers: [DogService],
})
export class DogModule {}
dog.service.ts
#Injectable()
export class DogService {
constructor(
private readonly animalService: AnimalService,
) {}
public makeSound() {
return this.animalService.makeSound()
}
}
cat.strategy.ts
class CatStrategy {
public makeSound() {
return 'meow';
}
}
export const catStrategy = new CatStrategy();
Repo that replicates the issue: https://github.com/kunukmak/nestjs-strategy-problem-example
To clarify, both catService.makeSound and dogService.makeSound return "meow" in this case. Is it possible to make the dog bark?
I think you are looking for something like the following. Check the repo for the full example here. You can see below, we are registering a DynamicModule from the AnimalModule class:
#Module({
imports: [AnimalModule.register()],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
The DynamicModule returned from the register() call is responsible for determining what implementation of the AnimalModule to provide. This means we can customize the AnimalModule based on configuration in the environment.
#Module({})
export class AnimalModule {
public static register(): DynamicModule {
const AnimalClassProvider = AnimalModule.getClassProvider();
return {
module: AnimalModule,
controllers: [AnimalController],
providers: [AnimalClassProvider],
exports: [AnimalClassProvider],
};
}
private static getClassProvider(): ClassProvider<AnimalService> {
const animalStrategy = process.env.ANIMAL_STRATEGY as AnimalStrategy;
const AnimalServiceClass = AnimalModule.getClassFromStrategy(animalStrategy);
return {
provide: AnimalService,
useClass: AnimalServiceClass,
};
}
private static getClassFromStrategy(strategy: AnimalStrategy): Type<AnimalService> {
switch (strategy) {
case AnimalStrategy.CAT: return CatService;
case AnimalStrategy.DOG: return DogService;
default: return AnimalService;
}
}
}
AnimalStrategy in this case is just an enum used to determine which implementation of the service we should provide.
With this approach, we allow Nest to construct the Provider along with all its dependencies properly. We are only responsible for telling Nest which implementation it will construct when it encounters an AnimalService dependency. This allows the rest of our application to be unaware of the implementation and only use our AnimalService abstraction.
From our AnimalController:
#Controller('animal')
export class AnimalController {
constructor(private readonly animalService: AnimalService) {}
#Post()
create(#Body() createAnimalDto: CreateAnimalDto) {
return this.animalService.create(createAnimalDto);
}
// ...
}
to another service in our application:
#Injectable()
export class PetOwnerService {
constructor(
private readonly animalService: AnimalService,
private readonly petOwnerService: PetOwnerService,
) {}
feedPet(petName: string) {
const petIsHungry = this.petOwnerService.isPetHungry(petName);
if (petIsHungry) this.animalService.feed(petName);
// ...
}
}

Can this kind of dependency injection be achieved with NestJS?

I want to inject the result of a Nest service method as dependency in a short way. Example is a logging facility, where a child logger is derived from the main logger with a new prefix.
It should be something like this (long version):
#Injectable()
class MyService {
private logger;
constructor(private loggerService: LoggerService) {
this.logger = loggerService.getChildLogger('prefix');
}
someMethod() {
this.logger.info('Hello');
}
}
But in a short version, something like this - maybe with a decorator:
#Injectable()
class MyService {
constructor(#logger('prefix') logger: LoggerService) {
}
someMethod() {
this.logger.info('Hello');
}
}
You could create providers for your loggers using factories injecting your LoggerService :
The LoggerService:
#Injectable()
export class LoggerService {
getChildLogger(scope: string) {
return new Logger(scope);
}
}
The MyService:
#Injectable()
export class MyService implements OnModuleInit {
constructor(#Inject('MY_SERVICE_LOGGER_TOKEN') public childLogger) {}
onModuleInit() {
this.childLogger.log('Hello World');
}
}
The module:
#Module({
providers: [
LoggerService,
MyService,
{
provide: 'MY_SERVICE_LOGGER_TOKEN',
useFactory: (loggerService) => loggerService.getChildLogger('scope'),
inject: [LoggerService]
},
]
})
export class MyModule {}
There is now a solution provided by Livio Brunner:
https://github.com/BrunnerLivio/nestjs-logger-decorator

Angular MatSnackBar not working from custom class

I am trying to create a toast using Material Snackbar from a custom class.
I am getting an error in my custom class ( Unable to find open from undefined. ) but working fine in user.service.ts
If ngZone is used, then i am getting an error, (unable to find run from undefined)
Note: In ErrorHandler Class
console.log(this.snackBar) // gives undefined
app.module.ts
providers: [ErrorHandler],
bootstrap: [AppComponent]
})
export class AppModule { }
User Service
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { catchError } from 'rxjs/operators';
import { environment } from '../../environments/environment';
import { ErrorHandler } from '../classes/error-handler';
import {MatSnackBar} from '#angular/material';
#Injectable({
providedIn: 'root'
})
export class UserService {
private url = environment.api+'/login';
constructor(private http: HttpClient, private eh:ErrorHandler, private sb: MatSnackBar) { }
login(credentials){
this.sb.open("hello world"); // Working Fine
return this.http.post(this.url, {})
.pipe(
catchError(this.eh.handleError)
);
}
}
Error Handler Class
import {Component, Injectable, NgZone} from '#angular/core';
import { HttpErrorResponse } from '#angular/common/http';
import { throwError } from 'rxjs';
import * as _ from 'lodash';
import {MatSnackBar} from '#angular/material';
#Injectable({
providedIn: 'root'
})
export class ErrorHandler {
constructor (private snackBar: MatSnackBar) {}
public handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
console.error('An error occurred:', error.error.message);
} else {
console.error("Error code working")
console.log(this.snackBar) // gives undefined
this.snackBar.open("Hello world"); // Throwing Error
}
// return an observable with a user-facing error message
return throwError('Something bad happened; please try again later.');
};
}
Thanks to all. I fixed it. Unfortunately i missed the stackoverflow link.
Change in
catchError((res) => this.eh.handleError(res))
did the trick
UserService
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { catchError } from 'rxjs/operators';
import { environment } from '../../environments/environment';
import { ErrorHandler } from '../classes/error-handler';
import {MatSnackBar} from '#angular/material';
#Injectable({
providedIn: 'root'
})
export class UserService {
private url = environment.api+'/login';
constructor(private http: HttpClient, private eh:ErrorHandler, private sb: MatSnackBar) { }
login(credentials){
this.sb.open("hello world"); // Working Fine
return this.http.post(this.url, {})
.pipe(
catchError((res) => this.eh.handleError(res)) // change in this line
);
}
}
I stumbled upon the same issue in my application a few days ago. The reason is the context of this.
In
This.sb.open("hello world"); // Working Fine
the context of this is the UserService class. While in
this.snackBar.open("Hello world"); // Throwing Error
the context of this changed. Probably to CatchSubscriber.
You already mentioned that:
catchError((res) => this.eh.handleError(res)) // change in this line
resolves the error. Because it changed the context of this back to the UserService class. An alternative solution is to use the arrow syntax for the whole login():
login = (credentials) => {
// your code goes here
}

How to integrate Neo4j database, NestJS framework and GraphQL?

I'm trying to integrate my REST API (NestJS) with new Neo4j database with GraphQL queries. Anybody succeed? Thanks in advance
EDIT 1: (I added my code)
import { Resolver } from "#nestjs/graphql";
import { Query, forwardRef, Inject, Logger } from "#nestjs/common";
import { Neo4jService } from "src/shared/neo4j/neoj4.service";
import { GraphModelService } from "./models/model.service";
import { Movie } from "src/graphql.schema";
#Resolver('Movie')
export class GraphService {
constructor(private readonly _neo4jService: Neo4jService) {}
#Query()
async getMovie() {
console.log("hello");
return neo4jgraphql(/*i don't know how get the query and params*/);
}
}
I am using a NestInterceptor to accomplish this:
#Injectable()
export class Neo4JGraphQLInterceptor implements NestInterceptor {
intercept(
context: ExecutionContext,
next: CallHandler<any>,
): Observable<any> | Promise<Observable<any>> {
const ctx = GqlExecutionContext.create(context);
return neo4jgraphql(
ctx.getRoot(),
ctx.getArgs(),
ctx.getContext(),
ctx.getInfo(),
);
}
}
To use it in your Resolver:
#Resolver('Movie')
#UseInterceptors(Neo4JGraphQLInterceptor)
export class MovieResolver {}
My GraphQLModule is configured like this:
#Module({
imports: [
GraphQLModule.forRoot({
typePaths: ['./**/*.gql'],
transformSchema: augmentSchema,
context: {
driver: neo4j.driver(
'bolt://neo:7687',
neo4j.auth.basic('neo4j', 'password1234'),
),
},
}),
],
controllers: [...],
providers: [..., MovieResolver, Neo4JGraphQLInterceptor],
})
Note the usage of transformSchema: augmentSchema to enable auto-generated mutations and queries (GRANDStack: Schema Augmentation)
Hope that helps a bit!
This is what works for me...not as elegant as I would like but it works; I want to have only one service/provider accessing my db not the service from each module even though that works also. So I am sticking with the Nest format of myModule->myResolver->myService-->Neo4jService. So Neo4jService is injected in all xService(s). I am using neo4jGraphql and augmentSchema and Cypher when necessary.
Code:
**appmodule.ts**
....
import { makeExecutableSchema } from 'graphql-tools';
import { v1 as neo4j } from 'neo4j-driver';
import { augmentTypeDefs, augmentSchema } from 'neo4j-graphql-js';
import { Neo4jService } from './neo4j/neo4j.service';
import { MyModule } from './my/my.module';
import { MyResolver } from './my/my.resolver';
import { MyService } from './my/my.service';
....
import { typeDefs } from './generate-schema'; // SDL type file
...
const driver = neo4j.driver('bolt://localhost:3000', neo4j.auth.basic('neo4j', 'neo4j'))
const schema = makeExecutableSchema({
typeDefs: augmentTypeDefs(typeDefs),
});
const augmentedSchema = augmentSchema(schema); // Now we have an augmented schema
#Module({
imports: [
MyModule,
GraphQLModule.forRoot({
schema: augmentedSchema,
context: {
driver,
},
}),
],
controllers: [],
providers: [ Neo4jService,
myResolver,
],
})
export class AppModule {}
**myResolver.ts**
import { Args, Mutation, Query, Resolver } from '#nestjs/graphql';
import { MyService } from './my.service';
#Resolver('My')
export class MyResolver {
constructor(
private readonly myService: MyService) {}
#Query()
async getData(object, params, ctx, resolveInfo) {
return await this.myService.getData(object, params, ctx, resolveInfo);
}
*//Notice I am just passing the graphql params, etc to the myService*
}
**myService.ts**
import { Injectable } from '#nestjs/common';
import { Neo4jService } from '../neo4j/neo4j.service';
#Injectable()
export class MyService {
constructor(private neo4jService: Neo4jService) {}
async getData(object, params, ctx, resolveInfo) {
return await this.neo4jService.getData(object, params, ctx, resolveInfo);
}
*// Again I am just passing the graphql params, etc to the neo4jService*
}
**neo4jService.ts**
import { Injectable } from '#nestjs/common';
import { neo4jgraphql } from 'neo4j-graphql-js';
#Injectable()
export class Neo4jService {
getData(object, params, ctx, resolveInfo) {
return neo4jgraphql(object, params, ctx, resolveInfo);
}
.....
......
}
So basically I postponed using neo4jgraphql until we arrive at neo4jService. Now all my DB calls are here.....as I said not elegant but it works.
Challenges: Graphql generate would not accept #relation...I found out that a change was made and now you need augmentTypeDefs.
...hope this helps
EDIT
Nestjs takes an awful long time to process the augmentSchema...so I would recommend skipping it..for now
Here is an example i created for (NestJS + GraphQL + Neo4j). I hope if this may help!
NestJS + GraphQL + Neo4j
I have not worked on GraphQL, but I know there is an npm package(Neo4j-graphql-js) to translate GraphQL queries into Cypher queries. It makes it easier to use GraphQL and Neo4j together.
Also, check GRANDstack it is a full-stack development integration for building Graph-based applications.
I suggest you to visit Neo4j Community.

Resources