Reputation: 5192
I use NodeJs/NestJs to build a RESTful service. I created some object to match with the request JSON. In these objects there are some optional attributes, but I would like to set default values to them if the client does not send them via JSON.
What is the best way to achieve the goal?
This is my DTO to match with JSON.
import { IsDefined, IsNumber, Min } from 'class-validator';
import { ApiModelProperty, ApiModelPropertyOptional } from '@nestjs/swagger';
export class RequestDto {
@IsDefined()
@IsNumber()
@Min(0)
@ApiModelProperty({description: 'The current age.'})
public CurrentAge: number;
@ApiModelPropertyOptional({description: 'The existing saving amount.'})
public ExistingSavingAmount: number = 0;
}
This is my NestJs controller
import { Controller, Post, Body, Param } from '@nestjs/common';
import { RequestDto } from './Dto/Request.Dto';
import { ApiResponse, ApiOperation } from '@nestjs/swagger';
@Controller('mycontroller')
export class MyController {
@Post('MyEndPoint')
@ApiOperation({ title: 'Do something' })
@ApiResponse({ status: 201, description: 'Something is done' })
public doSomething(@Body() request: RequestDto) {
// do more jobs
}
}
I launch the service, and post following JSON to my end point
{
"CurrentAge": 40,
}
In my controller I see ExistingSavingAmount
is blank in stead of having value of 0. But if I instantiate the RequestDto
directly I could see the value of ExistingSavingAmount
is 0.
Upvotes: 3
Views: 8785
Reputation: 60567
Your default value will only apply if RequestDto
is actually instantiated as a class. Since you're already using class-validator for validation, you can use classTransformer.plainToClass()
to instantiate the class.
If you're using the built-in ValidationPipe
, you can use the { transform: true }
option, to automatically instantiate your RequestDto
class:
@UsePipes(new ValidationPipe({ transform: true }))
@Post('MyEndPoint')
public doSomething(@Body() request: RequestDto) {
or as a global pipe:
async function bootstrap() {
const app = await NestFactory.create(ApplicationModule);
app.useGlobalPipes(new ValidationPipe({ transform: true }));
await app.listen(3000);
}
bootstrap();
Upvotes: 8
Reputation: 36349
OK, without code samples from the OP, the fidelity of this response may need improvement. That said, the "nest-y" way to do this is through a TransformPipe.
The canonical example they give is for the ParseIntPipe:
import { Injectable, BadRequestException} from '@nestjs/common';
@Injectable()
export class ParseIntPipe {
transform(value, metadata) {
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException('Validation failed');
}
return val;
}
}
Without knowing what your defaults look like, I'm going to assume it's something like a product, and you want to default some things and put some things as an empty string:
import { Injectable, BadRequestException} from '@nestjs/common';
// we will assume you have your own validation for the non-optional bits
const optionalDefaults = {
description: '',
category: 'Miscelleneous'
}
@Injectable()
export class ProductDefaultsPipe {
transform(value, metadata) {
const val = Object.assign(optionalDefaults, value);
return val;
}
}
Now, that said you might be using something that provides schemas and model definitions (like Joi or Mongoose). If you are, then I'd recommend setting all the defaults and validations in that schema and then applying the schema in your TransformPipe rather than writing much custom code at all. For example, if you have a ProductSchema, this would work for you:
@Injectable()
export class ProductDefaultsPipe {
async transform(value, metadata) {
const val = new Product(value);
const isValid = await val.validate();
if (!isValid) {
throw new BadRequestException('Validation failed');
}
return val;
}
}
Upvotes: 2