class-validator
class-validator copied to clipboard
feature: Allow field whitelisting to be turned off for selected nested objects
Description
I'd like to be able to turn off the whitelist option for nested objects.
If I have classes like:
class Dto {
@IsObject()
@ValidateNested()
@Type(() => NestedDto)
nested: NestedDto;
}
class NestedDto {
@IsString()
field: string;
}
I want to validate something like:
const object = {
nested: { field: "nice", random: "other" }
}
I want object.nested.random === "other" after validation with whitelist=true. Today, that field will be undefined. I also want object.nested.field to still be validated as a string, and throw errors if it's not. In other words, I want to validate the defined fields in the nested object but still retain any undefined fields.
Proposed solution
I was thinking a whitelist argument could be added to the ValidationOptions in ValidateNested. If undefined, we just respect the option that the initial validate function was called with.
Hello, is this a feature that was ever considered? I see the issue is still opened after more than 2 years
@juliusiv @willbouch I think for nestJs, I found the best work around to achieve this by following this comment: https://github.com/nestjs/nest/issues/2390#issuecomment-517623971
I've updated it to also received global config options while initializing globally and update the validation options by following the pattern below:
// rewrite-validation-options.decorator.ts
import {
ArgumentMetadata,
Injectable,
SetMetadata,
ValidationPipe,
ValidationPipeOptions,
} from '@nestjs/common';
import { ValidatorOptions } from 'class-validator';
import { Reflector } from '@nestjs/core';
export const REWRITE_VALIDATION_OPTIONS = 'rewrite_validation_options';
export function RewriteValidationOptions(options: ValidatorOptions) {
return SetMetadata(REWRITE_VALIDATION_OPTIONS, options);
}
@Injectable()
export class UpdatableValidationPipe extends ValidationPipe {
private readonly defaultValidatorOptions: ValidatorOptions;
constructor(private reflector: Reflector, globalOptions: ValidationPipeOptions = {}) {
super(globalOptions);
// Store only class-validator relevant options
this.defaultValidatorOptions = {
whitelist: globalOptions.whitelist,
forbidNonWhitelisted: globalOptions.forbidNonWhitelisted,
skipMissingProperties: globalOptions.skipMissingProperties,
forbidUnknownValues: globalOptions.forbidUnknownValues,
};
}
async transform(value: any, metadata: ArgumentMetadata) {
const overrideOptions = this.reflector.get<ValidatorOptions>(
REWRITE_VALIDATION_OPTIONS,
metadata.metatype
);
if (overrideOptions) {
const originalOptions = { ...this.validatorOptions };
this.validatorOptions = { ...this.defaultValidatorOptions, ...overrideOptions };
try {
const result = await super.transform(value, metadata);
this.validatorOptions = originalOptions;
return result;
} catch (err) {
this.validatorOptions = originalOptions;
throw err;
}
}
return super.transform(value, metadata);
}
}
Then use it in the following way in the global interceptor:
const reflector = app.get(Reflector);
app.useGlobalPipes(new UpdatableValidationPipe(reflector, { whitelist: true, transform: true }));
And to use it in your DTO's do the following:
@RewriteValidationOptions({ whitelist: false })
export class MyDto {
@ApiProperty({
.
.
.
}
I think this would be the best way forward since the NestJs team mentions they don't see any time soon implementing the feature here: https://github.com/nestjs/nest/issues/7779