Nest could not find PrismaService element (this provider does not exist in the current context) - nestjs

I'm trying to get PrismaService on my main.ts, but it's keep crashing. I'm new on this, can anyone help me to solve it?
My prisma.service.ts:
import { INestApplication, Injectable, OnModuleInit } from '#nestjs/common';
import { PrismaClient } from '#prisma/client';
#Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close();
});
}
}
My main.ts:
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import { PrismaService } from './prisma.service';
import { ValidationPipe } from '#nestjs/common';
import helmet from 'helmet';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors({
allowedHeaders: '*',
origin: '*',
});
app.use(helmet());
app.use(helmet.hidePoweredBy());
app.use(helmet.contentSecurityPolicy());
const prismaService = app.get(PrismaService);
await prismaService.enableShutdownHooks(app);
app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
}),
);
await app.listen(process.env.PORT, () => console.log('runing...'));
}
bootstrap();
The error message:
Error: Nest could not find PrismaService element (this provider does not exist in the current context)
at InstanceLinksHost.get (/home/rafittu/wophi/alma/back/node_modules/#nestjs/core/injector/instance-links-host.js:15:19)
at NestApplication.find (/home/rafittu/wophi/alma/back/node_modules/#nestjs/core/injector/abstract-instance-resolver.js:8:60)
at NestApplication.get (/home/rafittu/wophi/alma/back/node_modules/#nestjs/core/nest-application-context.js:64:20)
at /home/rafittu/wophi/alma/back/node_modules/#nestjs/core/nest-factory.js:133:40
at Function.run (/home/rafittu/wophi/alma/back/node_modules/#nestjs/core/errors/exceptions-zone.js:10:13)
at Proxy.<anonymous> (/home/rafittu/wophi/alma/back/node_modules/#nestjs/core/nest-factory.js:132:46)
at Proxy.<anonymous> (/home/rafittu/wophi/alma/back/node_modules/#nestjs/core/nest-factory.js:181:54)
at bootstrap (/home/rafittu/wophi/alma/back/src/main.ts:18:29)
When I delete PrismaService from main.ts, server start normaly

Use app.get(PrismaService, { strict: false }). The strict: false is to say that AppModule doesn't directly provider the provider and to traverse the DI container to find it

After setting up the PrismaService in prisma.service.ts and enabling shutdown hooks in the main.ts file, you also need to do the following:
In the app.module.ts you need to add PrismaService as one of the providers:
...
import { PrismaService } from './prisma/prisma.service';
#Module({
imports: [],
controllers: [AppController],
providers: [AppService, PrismaService], // add PrismaService here
})
export class AppModule {}

Related

NestJS unitesting Middleware

I'm trying to unit test a middleware to avoid sending a request but I'm actually not able to retreive the middleware with .get
import { NestApplicationContext } from '#nestjs/core';
import { BodyParserMiddleware } from '../../../src/config/body-parser/body-parser.middleware';
import { FeaturesModule } from '../../../src/features/features.module';
describe('CatsController', () => {
let app: NestApplicationContext;
beforeAll(() => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
});
describe('findAll', () => {
it('should return an array of cats', async () => {
console.log(app.get(BodyParserMiddleware));
});
});
});
my app module looks like this
import { Module } from '#nestjs/common';
import { FeaturesModule } from './features/features.module';
#Module({
imports: [FeaturesModule],
})
export class AppModule {}
and the last one feature module
import { MiddlewareConsumer, Module, NestModule } from '#nestjs/common';
import { BodyParserMiddleware } from '../config/body-parser/body-parser.middleware';
#Module({
imports: [
AuthModule,
],
})
export class FeaturesModule implements NestModule {
configure(consumer: MiddlewareConsumer): void {
consumer.apply(BodyParserMiddleware).forRoutes('api/v*/*/get-many');
}
}
Nest could not find BodyParserMiddleware element (this provider does not exist in the current context)
this is the error that im having
anyone knows how to retreive the middleware from the app container?
Also when I try to get the Features module to see if there is any clue there where I can go I receive
FeaturesModule {}

How to mock PrismaService for e2e in NestJs?

I'm usign nestjs-prisma in NestJs and I have in the following import for the prisma module in the app.module, so in every service I can use the PrismaService as the library allows it.
app.module.ts
import { Module } from '#nestjs/common';
import { PrismaModule } from 'nestjs-prisma';
import { CategoryModule } from './category/category.module';
#Module({
imports: [PrismaModule.forRoot({ isGlobal: true }), CategoryModule],
})
export class AppModule {}
I want to do a e2e test just for the CategoryModule and need to mock the PrismaService, how can i do that?
You need to mook first of all the PrismaModule and then import in it the PrismaService, here is an example.
category.e2e-spec.ts
import { Test, TestingModule } from '#nestjs/testing';
import { INestApplication } from '#nestjs/common';
import * as request from 'supertest';
import { PrismaModule, PrismaService } from 'nestjs-prisma';
import { CategoryModule } from '../src/category/category.module';
describe('CategoryModule (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
// Here you have to mock the category funtions that you'll use
const mockPrismaService = {
provide: PrismaService,
useFactory: () => ({
category: {
findMany: jest.fn(() => [])
},
}),
};
// Here is the creation of the module and the definition for the service
const mockPrismaModule = {
module: PrismaModule,
providers: [mockPrismaService],
global: true,
};
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [CategoryModule, mockPrismaModule], // Here is the import for the mock module
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('(GET) /category', () => {
return request(app.getHttpServer()).get('/category').expect(200);
});
});

nestjs jwt implementation is stuck - Nest can't resolve dependencies of the JWT_MODULE_OPTIONS (?)

I'm trying to implement JWT with NestJs. In my user.module.ts, I have added following configuration:
import { Module } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { UserService } from './user.service';
import { UserResolver } from './user.resolver';
import { User } from './entities/user.entity';
import { TypeOrmModule } from '#nestjs/typeorm';
import { AuthHelper } from './auth/auth.helper';
import { JwtStrategy } from './auth/auth.strategy';
import { PassportModule } from '#nestjs/passport';
import { JwtModule } from '#nestjs/jwt';
#Module({
imports: [
// PassportModule.register({ defaultStrategy: 'jwt', property: 'user' }),
// JwtModule.registerAsync({
// inject: [ConfigService],
// useFactory: (config: ConfigService) => ({
// secret: 'secret',
// signOptions: { expiresIn: 36000000 },
// }),
// }),
TypeOrmModule.forFeature([User]),
],
providers: [UserResolver, UserService], // AuthHelper, JwtStrategy],
})
export class UserModule {}
Whenever I uncomment these lines, I get some issues.
Here are some relevant files:
auth.strategy.ts
import { Injectable, Inject } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { PassportStrategy } from '#nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { User } from '../entities/user.entity';
import { AuthHelper } from './auth.helper';
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
#Inject(AuthHelper)
private readonly helper: AuthHelper;
constructor(#Inject(ConfigService) config: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'KhubSecret',
ignoreExpiration: true,
});
}
private validate(payload: string): Promise<User | never> {
return this.helper.validateUser(payload);
}
}
auth.guard.ts
import { Injectable, ExecutionContext } from '#nestjs/common';
import { AuthGuard, IAuthGuard } from '#nestjs/passport';
import { User } from '../entities/user.entity';
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') implements IAuthGuard {
public handleRequest(err: unknown, user: User): any {
return user;
}
public async canActivate(context: ExecutionContext): Promise<boolean> {
await super.canActivate(context);
const { user } = context.switchToHttp().getRequest();
return user ? true : false;
}
}
auth.helper.ts:
import {
Injectable,
HttpException,
HttpStatus,
UnauthorizedException,
} from '#nestjs/common';
import { JwtService } from '#nestjs/jwt';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcryptjs';
import { User } from '../entities/user.entity';
#Injectable()
export class AuthHelper {
#InjectRepository(User)
private readonly repository: Repository<User>;
private readonly jwt: JwtService;
constructor(jwt: JwtService) {
this.jwt = jwt;
}
public async decode(token: string): Promise<unknown> {
return this.jwt.decode(token, null);
}
public async validateUser(decoded: any): Promise<User> {
return this.repository.findOne(decoded.id);
}
public generateToken(user: User): string {
return this.jwt.sign({
id: user.userId,
username: user.username,
});
}
public isPasswordValid(password: string, userPassword: string): boolean {
return bcrypt.compareSync(password, userPassword);
}
public encodePassword(password: string): string {
const salt: string = bcrypt.genSaltSync(10);
return bcrypt.hashSync(password, salt);
}
private async validate(token: string): Promise<boolean | never> {
const decoded: unknown = this.jwt.verify(token);
if (!decoded) {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
const user: User = await this.validateUser(decoded);
if (!user) {
throw new UnauthorizedException();
}
return true;
}
}
I get some error like this:
[Nest] 18360 - 05/10/2022, 18:14:42 ERROR [ExceptionHandler] Nest can't resolve dependencies of the JWT_MODULE_OPTIONS (?). Please make sure that the argument ConfigService at index [0] is available in the JwtModule context.
Potential solutions:
- If ConfigService is a provider, is it part of the current JwtModule?
- If ConfigService is exported from a separate #Module, is that module imported within JwtModule?
#Module({
imports: [ /* the Module containing ConfigService */ ]
})
Error: Nest can't resolve dependencies of the JWT_MODULE_OPTIONS (?). Please make sure that the argument ConfigService at index [0] is available in the JwtModule context.
Potential solutions:
- If ConfigService is a provider, is it part of the current JwtModule?
- If ConfigService is exported from a separate #Module, is that module imported within JwtModule?
#Module({
imports: [ /* the Module containing ConfigService */ ]
})
I have already tested and implemented these solutions:
NestJS can't resolve dependencies of the JWT_MODULE_OPTIONS
Here is the solution. I hope it would help.
In your auth strategy add this:
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'KhubSecret',
ignoreExpiration: true,
});
}
instead of:
constructor(#Inject(ConfigService) config: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'KhubSecret',
ignoreExpiration: true,
});
}
In Short, Remove
#Inject(ConfigService) config: ConfigService
from your constructor
From the immediate error you've provided, I'll assume that your ConfigModule is not global. This means that you'll need to import the ConfigModule in the JwtModule.registerAsync() call (in the imports array). Another option would be to make your ConfigModule global (#nestjs/config has an option for this) so that you already have access to ConfigService.

How to mock configService as registerAs in my unit tests in nestjs

Hi I currently want to mock my configService injected as RegisterAs which allows me to have strong typing when accessing my environment variables, my problem with this is that when I mock the configService in my unit test and try to manipulate the environment values ​​it doesn't I'm making it.
I show some of what I have.
My Nestjs Project:
src/
|- config/
| |- environment.ts
|- modules/
| |- auth/
| |- auth.service.ts
| |- auth.service.spec.ts
|- app.module.ts
|- main.ts
|+- ...
app.module.ts
import * as Joi from 'joi';
import { AuthModule } from './modules/auth/auth.module';
import { ConfigModule } from '#nestjs/config';
import { Module } from '#nestjs/common';
import { ScheduleModule } from '#nestjs/schedule';
import env from './config/environment';
#Module({
imports: [
ScheduleModule.forRoot(),
ConfigModule.forRoot({ // <= I load the variables and validate them with Joi
isGlobal: true,
load: [env],
validationSchema: Joi.object().keys({
JWT_SESSION: Joi.string().trim(),
JWT_SESSION_EXP: Joi.number(),
}),
}),
AuthModule,
],
})
export class AppModule {}
config/environment.ts
import { registerAs } from '#nestjs/config';
export default registerAs('env', () => ({
jwtSession: process.env.JWT_SESSION || undefined,
expSession: Number(process.env.JWT_SESSION_EXP) || undefined,
}));
auth.service.ts
import * as moment from 'moment';
import { ConfigType } from '#nestjs/config';
import { Inject, Injectable, Logger } from '#nestjs/common';
import { AuthResponse } from './interfaces/login.interface';
import { AuthenticationDto } from './dto/auth.dto';
import { HttpService } from '#nestjs/axios';
import { JwtFidelity } from './interfaces/jwt-fidelity.interface';
import env from './../../config/environment';
import jwt_decode from 'jwt-decode';
import { lastValueFrom } from 'rxjs';
#Injectable()
export class AuthService {
constructor(
#Inject(env.KEY) private configService: ConfigType<typeof env>, // <= I inject the environment variables like this
) {}
isTokenExpired(): boolean { // <= This checks if the token has expired
if (!this.configService.jwtSession) return true;
if (!this.configService.expSession) return true;
return false;
}
}
auth.service.spec.ts
Note: this is where i would like you to help me.
import { ConfigModule, ConfigService } from '#nestjs/config';
import { HttpModule, HttpService } from '#nestjs/axios';
import { Test, TestingModule } from '#nestjs/testing';
import { AuthService } from '../auth.service';
import environment from '../../../config/environment';
import { of } from 'rxjs';
const env = () => ({
auth: {
partner: {
jwtSeesion:
'jwt-token',
expSession: '1661878302',
},
},
});
describe('AuthService', () => {
let service: AuthService;
let httpService: HttpService;
let configService: ConfigService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [HttpModule, ConfigModule.forRoot({ load: [environment] })],
providers: [
HttpService,
AuthService,
ConfigService,
{ provide: 'CONFIGURATION(env)', useFactory: env },
],
})
.overrideProvider(HttpService)
.useValue({ request: jest.fn() })
.overrideProvider(ConfigService)
.useValue({ jwtSession: jest.fn() })
.compile();
service = module.get<AuthService>(AuthService);
httpService = module.get<HttpService>(HttpService);
configService = module.get<ConfigService>(ConfigService);
});
it('should be defined', () => {
expect(service).toBeDefined();
expect(httpService).toBeDefined();
expect(configService).toBeDefined();
});
describe('AuthController.isTokenExpired', () => {
it('variable jwtSession undefined', async () => {
configService.jwtSession = undefined; // <= I would like the possibility of doing something like that
jest.spyOn(configService, 'jwtSession').mockReturnValue('hola' as any); // <= Or something like that
const isExpired = await service.isTokenExpired();
expect(isExpired).toBeTruthy();
});
});
});
I would like that at the level of each test I can be able to manipulate the value of the environment variables that I have injected with #Inject(env.KEY) private configService: ConfigType<typeof env>, in my auth.service.ts

Integrating Auth0 with NestJS - am unable to verify the access token within my API

In brief, I'm attempting to develop a GraphQL API that is guarded by an access token generated upon successful login. I'm copying and pasting the token into Postman and sending it with requests to a guarded resolver endpoint as an authorization header. I've verified that the token is indeed reaching the server as expected. The problem is that I seem to be unable to actually verify the token. Note that interpolation of things like environment variables, etc., is working and the application is compiling successfully. It's just that I'm not sure how to validate the access token provided by Auth0. I could really use some help with this and would even be willing to offer a reward to anyone who could help me solve this matter. Thanks!
My setup looks like this:
app.module.ts
src/authorization
- auth.guard.ts
- authorization.module.ts
- jwt.strategy.ts
src/products
- products.resolver.ts
That's it. Pretty simple. As to the files' contents:
// src/app.module.ts
/* istanbul ignore file */
import { Module } from '#nestjs/common';
import { GraphQLModule } from '#nestjs/graphql';
import { ConfigModule } from '#nestjs/config';
import { ProductsModule, ProductsService, ProductsResolver } from './products';
import { PrismaService } from './prisma.service';
import { AuthorizationModule } from './authorization/authorization.module';
#Module({
imports: [
GraphQLModule.forRoot({
debug: false,
playground: true,
typePaths: ['./**/*.graphql'],
definitions: { path: [process.cwd(), 'src/graphql.ts'].join() },
}),
PrismaService,
ConfigModule.forRoot({
isGlobal: false,
envFilePath: ['.env.development.local', '.env.test'],
}),
AuthorizationModule,
],
providers: [ProductsModule, ProductsResolver, ProductsService, PrismaService],
})
export class AppModule {}
// src/authorization/auth.guard.ts
import { ExecutionContext, UnauthorizedException } from '#nestjs/common';
import { GqlExecutionContext } from '#nestjs/graphql';
import { AuthGuard } from '#nestjs/passport';
// I'm not sure what to do here!
export class Auth0Guard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext) {
try {
const graphqlContext = GqlExecutionContext.create(context);
console.log(graphqlContext.getContext().req);
const [_, token] = graphqlContext
.getContext()
.req.headers.authorization.split(' ');
if (token) return true;
} catch (error) {
throw new UnauthorizedException();
}
}
}
// src/authorization/authorization.module.ts
import { Module } from '#nestjs/common';
import { PassportModule } from '#nestjs/passport';
import { ConfigModule } from '#nestjs/config';
import { Auth0Guard } from './auth.guard';
import { JwtStrategy } from './jwt.strategy';
#Module({
imports: [
PassportModule.register({
defaultStrategy: 'jwt'
}),
ConfigModule.forRoot({
envFilePath: ['.env.development.local', '.env.test'],
}),
],
providers: [JwtStrategy, Auth0Guard],
exports: [PassportModule, Auth0Guard],
})
export class AuthorizationModule {}
// src/authorization/jwt.strategy.ts
import { Injectable } from '#nestjs/common';
import { PassportStrategy } from '#nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '#nestjs/config';
import { passportJwtSecret } from 'jwks-rsa';
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(configService: ConfigService) {
super({
secretOrKeyProvider: passportJwtSecret({
cache: false,
rateLimit: true,
jwksRequestsPerMinute: 100,
jwksUri: configService.get('AUTH0_JWKS'),
}),
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
audience: configService.get('AUTH0_AUDIENCE'),
issuer: configService.get('AUTH0_ISSUER'),
passReqToCallback: false,
algorithms: ['RS256'],
});
}
// This is never envoked!
async validate(req: any, payload: any) {
console.log('Inside validate method', req, payload);
return payload;
}
}
// src/products/products.resolver.ts
import {
Query,
Mutation,
Resolver,
Args,
ArgsType,
Field,
} from '#nestjs/graphql';
import { UseGuards } from '#nestjs/common';
import { Auth0Guard } from '../authorization/auth.guard';
import { ProductsService } from './products.service';
#ArgsType()
#Resolver('Product')
export class ProductsResolver {
constructor(private productsService: ProductsService) {}
#Query('products')
// I use the guard I've created instead of the AuthGuard('jwt') because mine
// extends AuthGuard('jwt'); the problem is: how do I now *verify/validate*
// the JWT itself? That's been the missing piece of the puzzle for some time.
#UseGuards(Auth0Guard)
async getProducts() {
return await this.productsService.getAllProducts();
}
}

Resources