Boolean parameter in request body is always true in NestJS api
Solution 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
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 3
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.
joe_inz
"No one in the brief history of computing has ever written a piece of perfect software. It's unlikely that you'll be the first." - Andy Hunt
Updated on July 21, 2022Comments
-
joe_inz almost 2 years
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 calledlaserMode
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 = false
laserMode = 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 classexport class Parameters { ... constructor() { console.log('this :', this); } ... }
I don't see the property!
Note: when
laserMode
is 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!!?