I am trying to access to Request object from within a Validation Pipe in nestjs
In order to verify uniqueness of certain fields, I require the ID/UUID parameters supplied with PUT/PATCH request (not available in the data structure itself)
any idea?
Currently, it is not possible to access the request object at all in a pipe. If you need the request you can use a guard or an interceptor.
If you are working on verifying uniqueness, that sounds like a part of business logic more than just about anything else, so I would put it in a service and handle the query to the database there. Just my two cents.
Edit 11/17/2020
After learning way more about how the framework works as a whole, technically it is possible to get the entire request object in a pipe. There are two ways to go about it.
Make the pipe #Injectable({ scope: Scope.REQUEST }) so that it is request scoped. This will end up creating a new pipe on each request, but hey, if that's your cup of tea then great.
Make a custom parameter decorator as custom decorators get completely passed into pipes as they are. Do note, that this could impact how the ValidationPipe is functioning if that is bound globally, at the class, or method level.
We can create a Pipe and access request object. We can move further and update the Body as well, if needed.
Following is an example scenario, where createdBy field should be added to the Body dynamically. Let's say user details are available from request:
// user.pipe.ts
import { Request } from 'express'
import { REQUEST } from '#nestjs/core'
import { Injectable, Inject, Scope, PipeTransform } from '#nestjs/common'
#Injectable({ scope: Scope.REQUEST })
export class UserPipe implements PipeTransform<any> {
constructor(#Inject(REQUEST) protected readonly request: Request) {}
transform(value) {
let email = this.request["user"].email;
value['createdBy'] = email;
return value
}
}
We can now use this in controller like this:
// someentity.controller.ts
#Post()
public async create(
#Body(SetUserPipe) dto: SomeEntityDto,
): Promise<SomeEntity> {
....
}
Related
We have a Nestjs app that listens to a queue and gets a message. Whenever we get a message, we use it to send an email. We have a DTO for that:
export class SendEmailDTO {
#Expose()
#IsDefined()
#IsString()
subject: string;
We are using a library aws-sdk where we receive a message anytime there's a message in a queue:
// import { validate } from 'class-validator';
#SqsMessageHandler(process.env.EMAIL_REQUEST_CONSUMER_QUEUE_NAME)
async handleMessage(message: AWS.SQS.Message) {
// here we want to run validations for message using our DTO.
const errors = await validate(message);
if (errors.length !== 0) {}
}
We managed to use the validate method and validate the message received. But we don't like this approach since we are running the validations ourselves.
We would like to have the validation tight to the DTO, to mimic what nestjs does. Something like this:
export class SendEmailDTO {
#Expose()
#IsDefined()
#IsString()
subject: string;
constructor(input: Input) {
const errors = validate(input);
Logger.debug(errors, '### in SendEmailDTO constructor! errors');
}
}
But unfortunately it does not work. We are not sure if this is the right direction.
how can we run the validations within the constructor?
validate returns a promise, we need to wait, but we don't know how to do it within the constructor
are we totally wrong with this direction, and we can not use the validate method within the constructor?. If not, how can we use the DTO outside the nestjs controller world?
I'm learning NestJS and now I'm working in a simple authentication application, at this point I configured global pipes to validations and I'm using dto classes for example to validate #Body() fields. I don't know if I can use DTO to validate #Request fields sent from login endpoint.
import { Controller, Post, Request, UseGuards } from '#nestjs/common';
import { AuthService } from './auth.service';
import { AuthGuard } from '#nestjs/passport';
#Controller()
export class AuthController {
constructor(private authService: AuthService) {}
#UseGuards(AuthGuard('local'))
#Post('auth/login')
async login(#Request() req: reqDto /* I would like to use DTO for validations*/) {
return this.authService.login(req.user);
}
}
PS: I'm using DTO to validate SingUp body In UserController.
#Post('/signup')
createUser(#Body() createUserDto: CreateUserDto) {
return this.userService.createUser(createUserDto);
}
#Request() and #Headers() are two decorators that skip validation via pipes. You can make a custom request decorator that does get called via pipes, but annotating the request object would be a lot of work. What would be better is to create a decorator that gets just the data you need off the request, and then make a DTO for that object and validate that as necessary
CONTEXT
I am looking for a way to force the programmer to validate the authorization of each parameter of a request route.
The objective is to avoid possible security errors due to forgetting to validate the authorization of a parameter.
Some of you could say that this belongs more to the development cycle than to the framework itself, that this should be solved by specifying who is authorized to access a resource in the task definition, and that the tests would verify that this authorization is carried out correctly, but could be forgotten. So some of you will think that this would be solved in the subsequent code review, but it could be forgotten. So some of you will think well that would be solved in the functional test, anyway it could be forgotten.
My goal is to make sure that the programmer cannot forget that he must be aware of the authorization of the resource, even if he has to actively ignore the authorization for resources that are public.
How do I intend to carry it out?
I thought the best way would be to set an interceptor just before entering the controller that checks that all parameters have been changed to an object like {nParameter: {value: nValue, validate: true}}. And in this way force the programmer to establish another interceptor before this one that does that mapping by setting the parameter as validated or not depending on the case.
The problem is that I don't see a way to set that interceptor globally and have it run last since having to enter it on every resource would take us back to where we started.
I want to think that with some observable magic it could be achieved, I have done this:
#Injectable()
export class SecurityInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest<Request>()
request.params = Object.entries(request.params)
.reduce((params, [key, value]) => {
params[key] = { value, validate: false }
return parameters
}, {} as any)
return next.handle().pipe(
map((response) => {
if (Object.values(request.params).some(({ validate }) => !validate)) {
throw new Error()
}
return response
}),
)
}
}
But I would like to prevent the controller from running if not all parameters are checked.
I am trying to build a decorator to "log" request info
export const Tracking = () => {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
const method = descriptor.value;
descriptor.value = async function(...args: any[]) {
console.log(/** Request info */)
console.log(/** Headers, Body, Method, URL...*/)
return method.call(this, ...args);
}
}
}
and try to use it on a controller method like this.
export class Controller {
#Get('/path')
#Tracking()
public async getData(#Headers('user') user: User) {
return this.service.getData(user.id);
}
}
If this is impossible, is there a way to apply interceptor to some method of controller?
Or is there a thread(like)-level context for request?
Thanks!!
Decorators don't have access to the request information because of what a decorator is. It's a higher order function that is called to set metadata for the class, class member, class method, or class method parameter. This metadata can be read at runtime, but it is called and set essentially as soon the file is imported. Due to this, there's no way to call a decorator on each request, even Nest's #Body() and #Req() are called at the time of import and read at the time of the request (actually earlier but that's besides the point).
What you're looking for here sounds more like an interceptor, like Micael Levi and hoangdv have already mentioned. The Nest docs show a basic logging example, and there are packages out there like #ogma/nestjs-module (disclaimer: I'm the author) that handle this request logging/tracking for you including the addition of correlation IDs.
I want to change an entity name from Person to Individual. I want to keep the old /person endpoint (for temporary backward compatibility) and add a new /individual endpoint.
What would be the easiest way to do it in Node.js using Nest?
I can copy the code but I'm hoping for a better solution that won't require duplication
The #Controller() decorator accepts an array of prefix, thus you can use like this:
import { Controller, Get } from '#nestjs/common';
#Controller(['person', 'individual'])
export class IndividualController {
#Get()
findAll(): { /* ... */ }
}
to me this is the easiest way.
source
In NestJS, We can have multiple routes for the whole controller or for a single route. This is supported for all HTTP methods (POST, GET, PATCH etc., )
#Controller(['route-1', 'route-2'])
export class IndividualController {
#Get(['/sub-route-1','/sub-route-2'])
public async getSomething(...){...}
All HTTP methods support either a single string route or an array of string routes. We could use this technique to deprecate a bad route and start introducing a better route without breaking the consumers immediately.
if you mean expressjs instead of jestjs (which is a testing framework), my approach would be the following:
simply exclude your controller code into a function and pass it to your routes.
// your controller code
const doSomethingWithPersonEntity = (req, res, next) => {
res.status(200).json(persons);
}
router.get("/person", doSomethingWithPersonEntity);
router.get("/individual", doSomethingWithPersonEntity);