I'm working on a backend using NestJS, (which is amazing btw). I have a 'standard get a single instance of an entity situation' similar to this example below.
@Controller('user') export class UserController { constructor(private readonly userService: UserService) {} .. .. .. @Get(':id') async findOneById(@Param() params): Promise<User> { return userService.findOneById(params.id); }
This is incredibly simple and works - however, if the user does not exist, the service returns undefined and the controller returns a 200 status code and an empty response.
In order to make the controller return a 404, I came up with the following:
@Get(':id') async findOneById(@Res() res, @Param() params): Promise<User> { const user: User = await this.userService.findOneById(params.id); if (user === undefined) { res.status(HttpStatus.NOT_FOUND).send(); } else { res.status(HttpStatus.OK).json(user).send(); } } .. ..
This works, but is a lot more code-y (yes it can be refactored).
This could really use a decorator to handle this situation:
@Get(':id') @OnUndefined(404) async findOneById(@Param() params): Promise<User> { return userService.findOneById(params.id); }
Anyone aware of a decorator that does this, or a better solution than the one above?
Make NestJs returns 404 when EntityNotFoundError exception is thrown. When using findOrFail() or findOneOrFail() from typeORM, a 500 error is returned if there is no entity (EntityNotFoundError). To make it returns a 404, use an exception filter as described in https://docs.nestjs.com/exception-filters .
As mentioned, the response status code is always 200 by default, except for POST requests which are 201. We can easily change this behavior by adding the @HttpCode(...) decorator at a handler level. Hint Import HttpCode from the @nestjs/common package.
The shortest way to do this would be
@Get(':id') async findOneById(@Param() params): Promise<User> { const user: User = await this.userService.findOneById(params.id); if (user === undefined) { throw new BadRequestException('Invalid user'); } return user; }
There is no point in decorator here because it would have the same code.
Note: BadRequestException
is imported from @nestjs/common
;
Edit
After some time with, I came with another solution, which is a decorator in the DTO:
import { registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint } from 'class-validator'; import { createQueryBuilder } from 'typeorm'; @ValidatorConstraint({ async: true }) export class IsValidIdConstraint { validate(id: number, args: ValidationArguments) { const tableName = args.constraints[0]; return createQueryBuilder(tableName) .where({ id }) .getOne() .then(record => { return record ? true : false; }); } } export function IsValidId(tableName: string, validationOptions?: ValidationOptions) { return (object, propertyName: string) => { registerDecorator({ target: object.constructor, propertyName, options: validationOptions, constraints: [tableName], validator: IsValidIdConstraint, }); }; }
Then in your DTO:
export class GetUserParams { @IsValidId('user', { message: 'Invalid User' }) id: number; }
Hope it helps someone.
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