Now to enable validators DTO in NEST JS - node.js

Im new in NEST JS, and now im try to include some validator in DTO'S
looks like:
// /blog-backend/src/blog/dto/create-post.dto.ts
import { IsEmail, IsNotEmpty, IsDefined } from 'class-validator';
export class CreatePostDTO {
#IsDefined()
#IsNotEmpty()
title: string;
#IsDefined()
#IsNotEmpty()
description: string;
#IsDefined()
#IsNotEmpty()
body: string;
#IsEmail()
#IsNotEmpty()
author: string;
#IsDefined()
#IsNotEmpty()
datePosted: string;
}
But when i excute the post service like:
{
"title":"juanita"
}
Its return good!
But the validators should show and error rigth?
My post controloler
#Post('/post')
async addPost(#Res() res, #Body() createPostDTO: CreatePostDTO) {
console.log(createPostDTO)
const newPost = await this.blogService.addPost(createPostDTO);
return res.status(HttpStatus.OK).json({
message: 'Post has been submitted successfully!',
post: newPost,
});
}
My main.ts
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(5000);
}
bootstrap();

Let's binding ValidationPipe at the application level, thus ensuring all endpoints are protected from receiving incorrect data. Nestjs document
Enable ValidationPipe for your application.
main.ts
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '#nestjs/common'; // import built-in ValidationPipe
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe()); // enable ValidationPipe`
await app.listen(5000);
}
bootstrap();

For validating requests through the incoming dtos, you can use the #UsePipes() decorator provided by NestJS. This decorator can be applied globally (for the complete project), on individual endpoints. This is what the NestJS documentation says about it -
Pipes, similar to exception filters, can be method-scoped, controller-scoped, or global-scoped. Additionally, a pipe can be param-scoped. In the example below, we'll directly tie the pipe instance to the route param #Body() decorator.
Thus using it for your POST endpoint will help validate the request.
Hope this helps.

You also can register global pipes in app.module.ts file like this:
providers: [
{
provide: APP_PIPE,
useValue: new ValidationPipe({
// validation options
whitelist: true,
}),
},
],

Related

How to create custom (separate file) repository in NestJS 9 with TypeORM 0.3.x

This is not a duplicate Q. Please don't mark this as that.
Following is not I want
import { EntityRepository, Repository } from "typeorm";
import { Test } from "./test.model";
import { Injectable } from "#nestjs/common";
#EntityRepository(Test)
export class TestRepository extends Repository<Test> {}
the #EntityRepository decorator is now deprecated.
I also don't want to make a fake repository like in here:
https://stackoverflow.com/a/73352265/5420070
Don't want this either as I've to extract manager from dataSource, I don't want this because I think this is not the best way.
export const UserRepository = dataSource.getRepository(User).extend({
// ^^^^^^^^^^ from where this came from
findByName(firstName: string, lastName: string) {
return this.createQueryBuilder("user")
.where("user.firstName = :firstName", { firstName })
.andWhere("user.lastName = :lastName", { lastName })
.getMany()
},
})
Found above in: https://orkhan.gitbook.io/typeorm/docs/custom-repository#how-to-create-custom-repository
I don't think this is in NestJS context.
What I want
Want to know right way to make custom repository in latest version of NestJS (v9) & TypeORM (v0.3). In #EntityRepository deprecation note, they said that need to extend the repo to create custom repo like someRepo.extend({}). I want to know how to do it in NestJS way
In order to achieve what you want, you could do something like the following.
This solution is inspired by the official NestJS docs related to this topic, with some customization.
Steps to achieve it:
Create your TypeOrm entity as usual, let's say UserEntity (user.entity.ts file)
Create a UserRepository class (user.repository.ts file)
Create a UserService class as usual (user.service.ts file)
Import the UserRepository into your UserService
Update UserModule in order to provide the UserRepository and needed UserEntity
Detailed implementation example
1. user.entity.ts file
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
#Entity()
export class UserEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
firstName: string;
#Column()
lastName: string;
#Column({ default: true })
isActive: boolean;
}
2. user.repository.ts file
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserEntity } from './user.entity';
export class UserRepository extends Repository<UserEntity> {
constructor(
#InjectRepository(UserEntity)
private userRepository: Repository<UserEntity>
) {
super(userRepository.target, userRepository.manager, userRepository.queryRunner);
}
// sample method for demo purposes
async findByEmail(email: string): Promise<UserEntity> {
return await this.userRepository.findOneBy({ email }); // could also be this.findOneBy({ email });, but depending on your IDE/TS settings, could warn that userRepository is not used though. Up to you to use either of the 2 methods
}
// your other custom methods in your repo...
}
3. & 4. user.service.ts file
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { UserRepository } from './user.repository';
import { UserEntity } from './user.entity';
#Injectable()
export class UserService {
constructor(
private readonly userRepository: UserRepository, // import as usual
) {}
findAll(): Promise<UserEntity[]> {
return this.userRepository.find();
}
// call your repo method
findOneByEmail(email: string): Promise<UserEntity> {
return this.userRepository.findByEmail({ email });
}
findOne(id: number): Promise<UserEntity> {
return this.userRepository.findOneBy({ id });
}
async remove(id: string): Promise<void> {
await this.userRepository.delete(id);
}
// your other custom methods in your service...
}
5. Updating UserModule (user.module.ts file)
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { UserEntity } from './user.entity';
#Module({
imports: [TypeOrmModule.forFeature([UserEntity])], // here we provide the TypeOrm support as usual, specifically for our UserEntity in this case
providers: [UserService, UserRepository], // here we provide our custom repo
controllers: [UserController],
exports: [UserService, UserRepository] // add this only if you use service and/or custom repo within another module/service
})
export class UserModule {}
With this in place, you should be able to import the UserModule in your AppModule and be able to both implement custom methods in the UserRepository and use them in the UserService. You should also be able to call the manager and queryRunnner of the custom repository.
Additional Note
If you need to directly call your UserRepository methods from within another module/service, then update UserModule to export the UserRepository
Hope it helps, don't hesitate to comment.
import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from "typeorm";
import { CustomBaseEntity } from "../core/custom-base.entity";//custom-made
#Entity({name: 'rcon_log', schema: 'dbo'})
export class LogEntity extends CustomBaseEntity{
------------your code-----------
}

NestJS - How does AuthGuard knows about the Passport Strategy?

I am having a hard time figuring out the NestJS and PassportJS combination when it comes to the authentication/authorization process, and I am a type of developer who does not like magic when it comes to developing.
Issue
Basically, my goal is to understand how does AuthGuard knows about the Passport Strategy being implemented in the project, it could be Local Strategy, or any other, for example JWT Strategy. I have two modules AuthModule and UserModule and this is how the AuthService looks like:
#Injectable()
export class AuthService {
constructor(private usersService: UsersService){}
async validateUser(username: string, password: string): Promise<any> {
const user = await this.usersService.findOne(username);
if (user && user.password === password) {
const {password, ...result} = user
return result
}
return null
}
}
UserService:
import { Injectable } from '#nestjs/common';
export type User = any;
#Injectable()
export class UsersService {
private readonly users = [
{
userId: 1,
username: 'John Marston',
password: 'rdr1',
},
{
userId: 2,
username: 'Arthur Morgan',
password: 'rdr2',
},
]
async findOne(username: string): Promise<User | undefined> {
return this.users.find(user => user.username === username)
}
}
Passport
After installing the packages for Passport and NestJS, I imported PassportModule and implemented the LocalStrategy and also imported that strategy as a provider inside the AuthModule
LocalStrategy:
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super()
}
async validate(username: string, password: string): Promise<any> {
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
#Module({
imports: [UsersModule, PassportModule],
providers: [AuthService, LocalStrategy]
})
export class AuthModule {}
Login route
import { Controller, Post, Request, UseGuards } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Controller()
export class AppController {
#UseGuards(AuthGuard('local'))
#Post('login')
async login(#Request() req) {
return req.user;
}
}
I understand everything up until this part. I do also understand how we get the req.user object etc. but I do not understand how does the AuthGuard knows, that we implemented Passport Local Strategy. Does it look through the files (sorry if this is dumb to say) and finds where we imported the PassportModule and also where we implemented the LocalStrategy since that class extends the PassportStrategy class, but also important to say, imported from passport-local.
I do understand that AuthGuard is a special type of Guard, but I am not sure if I understand it properly.
I have a rather good article on this, but to put it on StackOverflow too:
Each Strategy from a passport-* package has a name property that is the name for the strategy. For passport-local that name is local. For passport-jwt, that name is 'jwt'. This doesn't always line up one to one, but each package should document what its name for passport is. This is the name that gets passed to passport.use() and passport.authenticate(). passport.use is called via some clever code in the PassportStrategy class from #nestjs/passport to register the strategy with passport and passport.authenticate is called inside of the AuthGuard() using either the global default set via the passport module's options or via the mixin's parameters, like local in your code sample.

TypeError : Cannot read property 'roles' of undefined NestJs

I try to block some root if it's not an admin, but when I run the code I have a TypeError but I don't know how to resolve it.
Thanks
roles.guards.ts
import { Injectable, CanActivate, ExecutionContext } from '#nestjs/common';
import { Reflector } from '#nestjs/core';
import { Role } from './role.enums';
import { ROLES_KEY } from './roles.decorator';
#Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) {
return true;
}
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((Role) => user.roles?.includes(Role));
}
}
articles.controllers.ts
#UseGuards(JwtAuthGuard)
#Roles(Role.Admin)
async addArticle(
#Body('title') artTitle: string,
#Body('description') artDescription: string,
#Body('url') artUrl: string,
#Body('cover') artCover: string,
#Body('content') artContent: string,
#Body('category') artCategory: string,
){
const generatedId = await this.articlesService.insertArticle(
artTitle,
artDescription,
artUrl,
artCover,
artContent,
artCategory
);
return { id: generatedId };
}
when I run the code I have a TypeError but I don't know how to resolve it.
Thanks
I'd like to add more detail to Jay McDoniel's answer since it still took me a few hours to get around this issue.
Create JWT.module.ts (JwtModule is already used by #nestjs/jwt hence my use of caps) file with the following:
import { ConfigModule, ConfigService } from "#nestjs/config";
import { JwtModule } from "#nestjs/jwt";
#Module({
imports: [
{
...JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
secretOrKeyProvider: () => (configService.get<string>("JWT_SECRET")),
signOptions: {
expiresIn: 3600,
},
}),
inject: [ConfigService],
}),
global: true
}
],
exports: [JwtModule]
})
export class JWTModule {}
Add this class to your app.module.ts's imports array.
if you have
{
provide: APP_GUARD,
useClass: RolesGuard,
},
in any of your modules... DELETE IT. declaring guards in any providers will automatically make it global and endpoints which you don't want to be guarded will end up getting guarded (I'm aware https://docs.nestjs.com/security/authorization#basic-rbac-implementation tells you to register the role guard in your providers but that just didn't work for me). You only need to import your strategies to the relevant routes.
In your controller, this should now work
#ApiBearerAuth()
#Roles(Role.Admin)
#UseGuards(JwtAuthGuard, RolesGuard)
#Get()
findAll() {
return this.usersService.findAll();
}
so this endpoint accepts users with a valid JWT and an admin role inside said JWT.
If I had to bet, your RolesGuard is bound to the global scope, whereas the JwtAuthGuard is bound to the route handler scope. Global guards execute first and foremost, so the RolesGuard executes before the JwtAuthGaurd can set req.user (passport is what does this under the hood). What you can do is either ensure that there is a req.user property (either via a middleware or jutt running the JwtAuthGuard globally) or you can move the RolesGuard to be scoped at the route handler level after the JwtAuthGuard runs.
Use JwtGuard and RoleGuard in the controller like #UseGuards(JwtAuthGuard, RolesGuard). The issue because of RoleGuards is not used in guard.
#Roles(Role.ADMIN)
#UseGuards(JwtAuthGuard,RolesGuard)
#Query(() => [User], { name: 'User' })
articles.module.ts
in this module file update in provider rolesGuards
providers: [AuthResolver, AuthService,LocalStrategy,JwtStrategy,RolesGuard],
use #Post() above your controller
#UseGuards(JwtAuthGuard)
#Roles(Role.Admin)
#Post('')
async addArticle(
#Body('title') artTitle: string,
#Body('description') artDescription: string,
#Body('url') artUrl: string,
#Body('cover') artCover: string,
#Body('content') artContent: string,
#Body('category') artCategory: string,
){
const generatedId = await this.articlesService.insertArticle(
artTitle,
artDescription,
artUrl,
artCover,
artContent,
artCategory
);
return { id: generatedId };
}

Is there a way to hide all the end-point in the controller.ts using a single decorator?

Currently, I am using #ApiExcludeEndpoint() ### on top of all methods to hide the end-point in the swagger-ui, like this:
import { Controller, Get, Query, Param } from '#nestjs/common';
import { ResourceService } from './resource.service';
import { Auth } from 'src/auth/auth.decorator';
import {
ApiTags,
ApiSecurity,
ApiOkResponse,
ApiForbiddenResponse,
ApiCreatedResponse,
ApiExcludeEndpoint
} from '#nestjs/swagger';
#Controller()
#ApiTags('Resources')
#ApiSecurity('apiKey')
export class ResourceController {
constructor(private readonly resourceService: ResourceService) {}
#Get('get_url')
#ApiExcludeEndpoint()
#Get()
#ApiOkResponse({
description: 'Resources list has succesfully been returned',
})
#ApiForbiddenResponse({ description: 'You are not allowed' })
#Auth(...common_privileges)
findAll(#Query() query: any): any {
......
}
#Get('get_url/:id')
#ApiExcludeEndpoint()
#ApiOkResponse({ description: 'Resource has succesfully been returned' })
#ApiForbiddenResponse({ description: 'You are not allowed' })
#Auth(...common_privileges)
findById(#Param('id') id: string, #Query() query: any): any {
......
}
}
I Need to know is there a way to hide all the end-point in the controller using a single decorator, I checked some documents it says to use #ApiIgnore() and #Hidden() but I can't find those in nestjs-swagger. Please comment on this
One possibility is to explicitly include the modules that you'd like to include in the swagger docs instead of just "including all modules" by default. Example:
const options = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.build();
const catDocument = SwaggerModule.createDocument(app, options, {
include: [LionsModule, TigersModule], // don't include, say, BearsModule
});
SwaggerModule.setup('api/cats', app, catDocument);
Without the explicit include:[] property, LionsModule, TigersModule, and BearsModule would be automatically included.
To hide all the end-point in the controller.ts, you must use ApiExcludeController instead of ApiExcludeEndpoint as in the example.
https://docs.nestjs.com/openapi/decorators
import { Controller, Get, Query, Param } from '#nestjs/common';
import { ResourceService } from './resource.service';
import { Auth } from 'src/auth/auth.decorator';
import {
ApiTags,
ApiSecurity,
ApiOkResponse,
ApiForbiddenResponse,
ApiCreatedResponse,
ApiExcludeController
// ApiExcludeEndpoint
} from '#nestjs/swagger';
#Controller()
#ApiTags('Resources')
#ApiSecurity('apiKey')
#ApiExcludeController()
export class ResourceController {
constructor(private readonly resourceService: ResourceService) {}
#Get('get_url')
// #ApiExcludeEndpoint()
#Get()
#ApiOkResponse({
description: 'Resources list has succesfully been returned',
})
#ApiForbiddenResponse({ description: 'You are not allowed' })
#Auth(...common_privileges)
findAll(#Query() query: any): any {
......
}
#Get('get_url/:id')
// #ApiExcludeEndpoint()
#ApiOkResponse({ description: 'Resource has succesfully been returned' })
#ApiForbiddenResponse({ description: 'You are not allowed' })
#Auth(...common_privileges)
findById(#Param('id') id: string, #Query() query: any): any {
......
}
}

SpyOn TypeORM repository to change the return value for unit testing NestJS

I would like to unittest corner cases for my TypeORM database calls. I have already mocked all my TypeORM repositories with valid data. But I would like to SpyOn the repository and change the return value form TypeORM. How do I do that?
import {INestApplication} from '#nestjs/common';
import {Test} from '#nestjs/testing';
import {CommonModule} from '#src/common/common.module';
import {AuthService} from './auth.service';
import {Repository} from 'typeorm';
import {V3User} from '#src/database/entity/user.v3entity';
describe('AuthService', () => {
let service: AuthService;
let app: INestApplication;
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [CommonModule.forRoot(`${process.env.DEV_ENV}`)],
providers: [
AuthService,
{provide: 'V3USER_REPOSITORY', useValue: mockRepositoryV3User()},
],
}).compile();
app = module.createNestApplication();
await app.init();
service = module.get<AuthService>(AuthService);
});
it('test auth service - with non existing user in v3 db', async () => {
jest.spyOn(?????? , 'findOne').mockImplementation(() => undefined);
const res = await service.loginUser("bad token");
await expect(service.tokenBasedAuth('example bad token'))
.rejects.toThrow('bad token exception');
});
});
I mock the database like this for normal test cases:
export const mockRepositoryV3User = () => ({
metadata: {
columns: [],
relations: [],
},
findOne: async () =>
Promise.resolve({
id: 3,
email: 'email#example.com',
first_name: 'david',
last_name: 'david',
last_login: '2019-07-15',
date_joined: '2019-07-15',
}),
});
Okay, after finally getting around to testing and playing with ideas I've found that this is a valid strategy
Assume we have set up a PhotoEntity with basic properties, nothing too special (id, name, description, etc.)
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
#Entity()
export class Photo {
#PrimaryGeneratedColumn()
id: number;
#Column({ length: 500 })
name: string;
#Column('text')
description: string;
#Column()
filename: string;
#Column('int')
views: number;
#Column()
isPublished: boolean;
}
Set up a PhotoService such as the following (super basic but it will illustrate the point):
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { Photo } from './photo.entity';
#Injectable()
export class PhotoService {
constructor(
#InjectRepository(Photo)
private readonly photoRepository: Repository<Photo>,
) {}
async findAll(): Promise<Photo[]> {
return await this.photoRepository.find();
}
}
We can useClass: Repository so that we don't have to do any of the heavy lifting of setting up a repository class to use for testing (Repository is imported from the TypeORM package. We can then get the repo from the module and save it to a value for easy mocking and set up our tests like so:
import { Test, TestingModule } from '#nestjs/testing';
import { PhotoService } from './photo.service';
import { getRepositoryToken } from '#nestjs/typeorm';
import { Photo } from './photo.entity';
import { Repository } from 'typeorm';
describe('PhotoService', () => {
let service: PhotoService;
// declaring the repo variable for easy access later
let repo: Repository<Photo>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
PhotoService,
{
// how you provide the injection token in a test instance
provide: getRepositoryToken(Photo),
// as a class value, Repository needs no generics
useClass: Repository,
},
],
}).compile();
service = module.get<PhotoService>(PhotoService);
// Save the instance of the repository and set the correct generics
repo = module.get<Repository<Photo>>(getRepositoryToken(Photo));
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should return for findAll', async () => {
// mock file for reuse
const testPhoto: Photo = {
id: 'a47ecdc2-77d6-462f-9045-c440c5e4616f',
name: 'hello',
description: 'the description',
isPublished: true,
filename: 'testFile.png',
views: 5,
};
// notice we are pulling the repo variable and using jest.spyOn with no issues
jest.spyOn(repo, 'find').mockResolvedValueOnce([testPhoto]);
expect(await service.findAll()).toEqual([testPhoto]);
});
});
Run your tests against the specified file or against all tests
▶ npm run test -- photo.service
> nestjs-playground#0.0.1 test ~/Documents/code/nestjs-playground
> jest "photo.service"
PASS src/photo/photo.service.spec.ts
PhotoService
✓ should be defined (17ms)
✓ should return for findAll (4ms) < -- test passes with no problem
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.372s, estimated 4s
Ran all test suites matching /photo.service/i.

Resources