Migrate from class-validator
Migrating from class-validator to Valibot is a bigger paradigm shift than for other libraries, since you are switching from decorated classes to schemas. In return, it removes a lot of boilerplate. You no longer need experimental decorators, reflect-metadata or class-transformer, and your schemas validate plain data directly, with the TypeScript type inferred automatically. The following guide will help you migrate step by step.
Replace classes with schemas
Each decorated class becomes a schema, and each decorator becomes a schema function or an action within a pipeline. Since the class no longer exists, you infer the TypeScript type from the schema with InferOutput.
// Change this
import { IsEmail, IsInt, IsString, Min } from 'class-validator';
class User {
@IsString()
name: string;
@IsEmail()
email: string;
@IsInt()
@Min(0)
age: number;
}
// To this
import * as v from 'valibot';
const UserSchema = v.object({
name: v.string(),
email: v.pipe(v.string(), v.email()),
age: v.pipe(v.number(), v.integer(), v.minValue(0)),
});
type User = v.InferOutput<typeof UserSchema>;Restructure code
With class-validator, you first convert plain data to a class instance, for example with class-transformer's plainToInstance, and then validate it. With Valibot, you validate plain data directly. The validate method, which resolves to an array of validation errors, is replaced by safeParse, which returns a result object. Since Valibot is synchronous by default, there is no need for a separate validateSync method.
// Change this
const user = plainToInstance(User, input);
const errors = await validate(user);
if (errors.length === 0) {
console.log(user);
} else {
console.log(errors);
}
// To this
const result = v.safeParse(UserSchema, input);
if (result.success) {
console.log(result.output);
} else {
console.log(result.issues);
}If you prefer an exception to be thrown on invalid input, as with validateOrReject, use parse instead.
// Change this
await validateOrReject(user);
// To this
const output = v.parse(UserSchema, input);We recommend that you read our mental model guide to understand how the individual functions of Valibot's modular API work together.
Optional properties
The @IsOptional decorator skips validation when the value is null or undefined. This matches Valibot's nullish schema. If a property can only be undefined but not null, use optional instead.
// Change this
class Profile {
@IsOptional()
@IsString()
bio?: string;
}
// To this
const ProfileSchema = v.object({
bio: v.nullish(v.string()),
});Nested objects and arrays
Instead of @ValidateNested in combination with class-transformer's @Type decorator, you nest schemas directly. For arrays, the { each: true } option is replaced by wrapping the item schema with array.
// Change this
class Order {
@IsArray()
@ValidateNested({ each: true })
@Type(() => Item)
items: Item[];
}
// To this
const OrderSchema = v.object({
items: v.array(ItemSchema),
});Change names
The following table shows how the most common decorators and methods map to Valibot's API.
| class-validator | Valibot |
|---|---|
@Contains | includes |
@IsArray | array |
@IsBase64 | base64 |
@IsBoolean | boolean |
@IsCreditCard | creditCard |
@IsDateString | isoDate, isoDateTime, isoTimestamp |
@IsDefined | Default behavior of every schema |
@IsEmail | email |
@IsEnum | enum |
@IsHexadecimal | hexadecimal |
@IsIn | picklist |
@IsInt | number with integer |
@IsIP | ip, ipv4, ipv6 |
@IsNegative | ltValue |
@IsNotEmpty | nonEmpty |
@IsNotIn | notValues |
@IsNumber | number |
@IsOptional | nullish |
@IsPositive | gtValue |
@IsString | string |
@IsUrl | url |
@IsUUID | uuid |
@Length | minLength with maxLength |
@Matches | regex |
@Max | maxValue |
@MaxLength | maxLength |
@Min | minValue |
@MinLength | minLength |
@Validate | check, checkAsync |
@ValidateNested | Nested schema |
validate | safeParse |
validateOrReject | parse |
validateSync | safeParse |
Many more specific decorators like @IsMobilePhone are backed by validator.js. For those without a direct Valibot equivalent, you can use check or regex to implement the same validation.
Other details
Below are some more details that may be helpful when migrating from class-validator to Valibot.
Unknown properties
By default, class-validator keeps properties without decorators on the instance. This matches looseObject. The whitelist: true option, which strips unknown properties, matches the default behavior of object. The forbidNonWhitelisted: true option, which rejects them, matches strictObject. See the objects guide for more details.
// Change this
const errors = await validate(post, {
whitelist: true,
forbidNonWhitelisted: true,
});
// To this
const result = v.safeParse(v.strictObject({ title: v.string() }), input);Custom error messages
Instead of the message property in the decorator options, you pass a single string as the first argument to schemas and as the last argument to actions. See the quick start guide for more details.
// Change this
class Login {
@MinLength(8, { message: 'Password is too short' })
password: string;
}
// To this
const LoginSchema = v.object({
password: v.pipe(v.string(), v.minLength(8, 'Password is too short')),
});Validation options
Some validation options map only approximately to Valibot and require attention to details. The skipMissingProperties option skips properties that are null or undefined, except for those marked with @IsDefined. Wrapping your schema with partial comes close, but only allows undefined and affects every entry. For full parity, wrap the individual entries with nullish instead. The stopAtFirstError option stops after the first error of each property, but still reports every invalid property. Valibot's abortEarly configuration is stricter and stops after the first issue overall. Validation groups have no direct equivalent. Instead, we recommend deriving multiple schemas from a shared object with methods like pick, omit and partial.
Framework integration
If you use class-validator through a framework like NestJS, you can replace its validation pipe with a custom pipe that calls parse with your schema. See the integration guide for an overview of ecosystem integrations.