Validate a nested array of objects GET query parameter using a DTO & class-validator in NestJS
Sending an array of objects as a GET request's query parameters using axios from the frontend to a NestJS backend, I ended up with an error:
"constraints":{"nestedValidation":"each value in nested property byRelationships must be either object or array"}
Backend-side
Try to figure out what it was I was doing wrong, I first enabled transform in the app's validation pipe.
app.useGlobalPipes(
new ValidationPipe({ transform: true }),
);
Then set @ValidateNested({ each: true })
, and @Type(() => YourClass
in my validation schema, like this.
import { DictionnaryOfValues } from './types';
import { IsIn, IsString } from 'class-validator';
import { Type } from 'class-transformer';
import { IsIn, IsOptional, IsString, ValidateNested } from 'class-validator';
export class NestedPropertyDto {
@IsString()
id: string;
@IsIn([...Object.values(DictionnaryOfValues)])
indexName: DictionnaryOfValues;
}
export class DtoClass {
@ValidateNested({ each: true })
@Type(() => NestedPropertyDto)
@IsOptional()
byRelationShips?: NestedPropertyDto[];
}
To no avail. I was still getting the error. Removing the @ValidateNested
and @Type
decorators from the validation schema in the backend, then outputing in the console the query parameter I actually received, I noticed it was not an object, but a stringified object, because...
Frontend-side
... in my frontend, axios didn't spontaneously serialize my query parameters the way I needed them The solution here was quite simple, but not necessarily obvious, and once again I have to thank a generous StackOverflow contributor for it:
- Use
paramSerializer
in the axios request - Serialize the query's parameters using the
qs
library
Like this:
axios.get('/myController/myAction', {
params: {
storeIds: [1,2,3]
},
paramsSerializer: params => {
return qs.stringify(params)
}
})
We still get the error. If we remove the @ValidateNested & @Type decorators in the backend, then output the actual query parameter received, we will notice it is not an object, but a stringified object. Because our frontend doesn't serialize it correctly. Let's fix that.
Epilogue
This took me more or less three to four hours to figure out. Perhaps because I have little frontend experience. Perhaps because I have little to no experience with either NestJS or class-validator. Nonetheless, I'd wager I'm not the only one around to have faced this, and I'd bet others will in the future. So, here you go; hope it helps 😉