Reputation: 25564
I am using class-validator package with NestJS and I am looking to validate an array of objects that need to have exactly 2 objects with the same layout:
So far I have:
import { IsString, IsNumber } from 'class-validator';
export class AuthParam {
@IsNumber()
id: number;
@IsString()
type: string;
@IsString()
value: string;
}
and
import { IsArray, ValidateNested } from 'class-validator';
import { AuthParam } from './authParam.model';
export class SignIn {
@IsArray()
@ValidateNested({ each: true })
authParameters: AuthParam[];
}
per @kamilg response (I am able to enforce exacly 2 elements):
import { IsArray, ValidateNested, ArrayMinSize, ArrayMaxSize } from 'class-validator';
import { AuthParam } from './authParam.model';
export class SignInModel {
@IsArray()
@ValidateNested({ each: true })
@ArrayMinSize(2)
@ArrayMaxSize(2)
authParameters: AuthParam[];
}
I still can pass an empty array or an array with some other objects not related to AuthParam.
How I should modify it get validation?
Also how I can enforce mandatory 2 elements in the array? MinLength(2) seems to be regarding string... (resolved)
Upvotes: 92
Views: 181663
Reputation: 2430
Without using @Type
annotation, following approach also works great:
import { IsString, IsNumber } from 'class-validator';
export class AuthParam {
@IsNumber()
id: number;
@IsString()
type: string;
@IsString()
value: string;
constructor(payload: AuthParam) {
this.id = payload.id;
this.type = payload.type;
this.value = payload.value;
}
}
and
import { IsArray, ValidateNested, ArrayMinSize, ArrayMaxSize } from 'class-validator';
import { AuthParam } from './authParam.model';
export class SignInModel {
@IsArray()
@ValidateNested({ each: true })
@ArrayMinSize(2)
@ArrayMaxSize(2)
authParameters: AuthParam[];
constructor(payload: SignInModel) {
if (isArray(payload.authParameters)) {
const authParameters = payload.authParameters.map(ap => new AuthParam(ap));
this.authParameters = authParameters;
} else {
this.authParameters = payload.authParameters;
}
}
}
Upvotes: 2
Reputation: 2568
I was able to validate array through this snippet
import { IsArray, ValidateNested} from 'class-validator';
import { Type } from 'class-transformer';
@IsArray()
@ValidateNested({ each: true })
@Type(() => TypeOfEachObject)
nameOfArray: TypeOfEachObject[];
Upvotes: 3
Reputation: 978
I Know I Am Late But Facing Some Issue With Type, Then Try Another Way To Implement This:
export class AuthParam {
@IsNumber()
id: number;
@IsString()
type: string;
@IsString()
value: string;
}
Validation function
@ValidatorConstraint()
export class IsAuthArray implements ValidatorConstraintInterface {
public async validate(authData: AuthParam[], args: ValidationArguments) {
return Array.isArray(authData) && authData.reduce((a, b) => a && (typeof b.id === "number") && typeof b.type === "string" && typeof b.field === "string", true);
}
}
export class SignInModel {
@IsNotEmpty()
@IsArray()
@ArrayMinSize(2)
@ArrayMaxSize(2)
@Validate(IsAuthArray, {
message: "Enter valid value .",
})
authParameters: AuthParam[];
}
Maybe It Will Help Someone 😃
Upvotes: 10
Reputation: 3204
Add @Type(() => AuthParam)
to your array and it should be working. Type
decorator is required for nested objects(arrays). Your code becomes
import { IsArray, ValidateNested, ArrayMinSize, ArrayMaxSize } from 'class-validator';
import { AuthParam } from './authParam.model';
import { Type } from 'class-transformer';
export class SignInModel {
@IsArray()
@ValidateNested({ each: true })
@ArrayMinSize(2)
@ArrayMaxSize(2)
@Type(() => AuthParam)
authParameters: AuthParam[];
}
Be careful if you are using any exception filter to modify the error reponse. Make sure you understand the structure of the class-validator errors.
Upvotes: 205
Reputation: 743
const param1: AuthParam = Object.assign(new AuthParam(), {
id: 1,
type: 'grant',
value: 'password'
})
const param2: AuthParam = Object.assign(new AuthParam(), {
id: 1,
type: 4,
value: 'password'
})
const signInTest = new SignInModel()
signInTest.authParameters = [param1, param2]
validate(signInTest).then(e => {
console.log(e[0].children[0].children[0])
})
This works correctly, this is:
ValidationError {
target: AuthParam { id: 1, type: 4, value: 'password' },
value: 4,
property: 'type',
children: [],
constraints: { isString: 'type must be a string' } }
so I may only assume that object which is being validated, is not an instance of AuthParam
const param2: AuthParam = {
id: 1,
type: 4,
value: 'password'
} as any
as expected, there aren't any decorators on this object (which may be true for Nest.js controllers and nested objects from body/req) - so validation is ignored.
Please check this (tl;dr - @Type
decorator form class-transformer
)
Upvotes: 2
Reputation: 743
You can use the following:
validator.arrayNotEmpty(array); // Checks if given array is not empty.
validator.arrayMinSize(array, min); // Checks if array's length is at least `min` number.
(https://github.com/typestack/class-validator#manual-validation)
You may want to consider writing custom validator which would better reflect the business requirement you have.
Upvotes: 3