'Boolean parameter in request body is always true in NestJS api
Consider this endpoint in my API:
@Post('/convert')
@UseInterceptors(FileInterceptor('image'))
convert(
@UploadedFile() image: any,
@Body(
new ValidationPipe({
validationError: {
target: false,
},
// this is set to true so the validator will return a class-based payload
transform: true,
// this is set because the validator needs a tranformed payload into a class-based
// object, otherwise nothing will be validated
transformOptions: { enableImplicitConversion: true },
}),
)
parameters: Parameters,
) {
return this.converterService.start(image, parameters);
}
The body of the request, which is set to parameters argument, contains a property called laserMode that should be a boolean type, it is validated like such on the parameters DTO:
@IsDefined()
@IsBoolean()
public laserMode: boolean;
now the strange part, when a send a request from PostMan where:
laserMode = falselaserMode = cool(a string other the boolean value)
I noticed that laserMode is always set to true and this is after the validation process is completed because when I console.log the instance of Parameter in the constructor of the class
export class Parameters {
...
constructor() {
console.log('this :', this);
}
...
}
I don't see the property!
Note: when
laserModeis removed from the request, the expected validation errors are returned (should be defined, should be boolean value).
// the logged instance 'this' in the constructor
this : Parameters {
toolDiameter: 1,
sensitivity: 0.95,
scaleAxes: 200,
deepStep: -1,
whiteZ: 0,
blackZ: -2,
safeZ: 2,
workFeedRate: 3000,
idleFeedRate: 1200,
laserPowerOn: 'M04',
laserPowerOff: 'M05',
invest: Invest { x: false, y: true }
}
// the logged laserMode value in the endpoint handler in the controller
parameters.laserMode in controller : true
// the logged laser value from the service
parameters.laserMode in service : true
- misspelling is checked
- same result is noticed when using a Vue app instead of postman. So!!?
Solution 1:[1]
This is how I got round the issue while managing to keep the boolean typing.
By referring to the original object by key instead of using the destructured value.
import { Transform } from 'class-transformer';
const ToBoolean = () => {
const toPlain = Transform(
({ value }) => {
return value;
},
{
toPlainOnly: true,
}
);
const toClass = (target: any, key: string) => {
return Transform(
({ obj }) => {
return valueToBoolean(obj[key]);
},
{
toClassOnly: true,
}
)(target, key);
};
return function (target: any, key: string) {
toPlain(target, key);
toClass(target, key);
};
};
const valueToBoolean = (value: any) => {
if (value === null || value === undefined) {
return undefined;
}
if (typeof value === 'boolean') {
return value;
}
if (['true', 'on', 'yes', '1'].includes(value.toLowerCase())) {
return true;
}
if (['false', 'off', 'no', '0'].includes(value.toLowerCase())) {
return false;
}
return undefined;
};
export { ToBoolean };
export class SomeClass {
@ToBoolean()
isSomething : boolean;
}
Solution 2:[2]
Found a workaround for the issue with class-transformer
You can use this:
@IsBoolean()
@Transform(({ value} ) => value === 'true')
public laserMode: boolean;
This will transform the string into a boolean value, based on if it is 'true' or any other string. A simple workaround, but every string different than true, results in false.
Solution 3:[3]
This is due to the option enableImplicitConversion. Apparently, all string values are interpreted as true, even the string 'false'.
There is an issue requesting a changed behavior for class-transformer.
Solution 4:[4]
If you want to receive both true/false,
then use the below solution.
It will mark all true for defined values
and mark it as false for all others
@Transform(({ value }) => {
return [true, 'enabled', 'true', 1, '1'].indexOf(value) > -1;
})
mode: boolean;
Avoid using below decorators as they don't work well enough
@IsBoolean()
@Type(() => Boolean)
Solution 5:[5]
The simplest solution I found is:
@IsBoolean()
@Type(() => Boolean)
laserMode: boolean;;
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Marcelo Kochiyama |
| Solution 3 | Kim Kern |
| Solution 4 | NAVPREET SINGH |
| Solution 5 | s4eed |
