
A beginner-friendly guide to every validation decorator in class-validator, grouped by what it actually checks.
The everyday building blocks — checking whether a value exists, is empty, or matches an allowed set.
| Decorator | What it does |
|---|---|
@IsDefined() |
Value must not be undefined or null. Runs even if you skip every other validator. |
@IsOptional() |
If the value is undefined/null, skip all other validators on this field. |
@IsNotEmpty() |
Value must not be "", null, or undefined. The one you'll use constantly. |
@IsEmpty() |
Value must be empty — rarely needed, but useful for "must be blank" fields. |
@Equals(value) |
Must equal one exact value. |
@NotEquals(value) |
Must not equal one exact value. |
@IsIn([...]) |
Must be one of a fixed list — an inline "enum" check. |
@IsNotIn([...]) |
Must not be one of a fixed, disallowed list of values. |
Beginner tip: Reach for
@IsNotEmpty()first on required fields, and@IsOptional()on anything that isn't required — it silences every other check when the field is missing.
Confirms the shape of the data itself — is it really a string, a number, an array? Usually the first decorator on any property.
| Decorator | What it does |
|---|---|
@IsString() |
Must be a string. |
@IsNumber() |
Must be a number (accepts decimals). |
@IsBoolean() |
Must be true or false. |
@IsInt() |
Must be a whole number — no decimals allowed. |
@IsArray() |
Must be an array (use with each: true to check items). |
@IsObject() |
Must be a plain JS object. |
@IsDate() |
Must be a real JavaScript Date object. |
@IsEnum(Enum) |
Must be a valid value from a TypeScript enum. |
@IsInstance(SomeClass) |
Must be an instance of a specific class — handy for validating nested value objects. |
Beginner tip: Pair a type check with a purpose check, e.g.
@IsString() @IsEmail()— the type check runs first, so later validators don't crash on the wrong type.
Once you know something is a number, these check which number is acceptable — ranges, sign, and divisibility.
| Decorator | What it does |
|---|---|
@IsPositive() |
Must be greater than 0. |
@IsNegative() |
Must be less than 0. |
@Min(18) |
Must be at least the given value. |
@Max(100) |
Must be at most the given value. |
@IsDivisibleBy(5) |
Must divide evenly by the given number — e.g. 10, 15, 20 for 5. |
// enforce adults only, capped at a sane age
class SignupDto {
@Min(18)
@Max(120)
age: number;
}
A small but useful pair — bounding a date field to a sensible window, like booking systems or expiry checks.
| Decorator | What it does |
|---|---|
@MinDate(date) |
The date must fall on or after the given date. Great for "no bookings in the past." |
@MaxDate(date) |
The date must fall on or before the given date. Great for expiry or cutoff limits. |
// only allow bookings within the next year
class BookingDto {
@MinDate(new Date())
@MaxDate(new Date(Date.now() + 365 * 24 * 60 * 60 * 1000))
visitDate: Date;
}
Beginner tip: Incoming JSON dates are usually strings, not
Dateobjects. Use@IsDateString()(from the string group) if you're validating a raw ISO string before it's transformed.
The biggest group by far — length, format, and pattern checks. Here are the ones you'll reach for as a beginner.
| Decorator | What it does |
|---|---|
@IsEmail() |
Must look like a valid email address. |
@IsUrl() |
Must be a valid URL. |
@Length(4, 20) |
String length must fall between min and max. |
@MinLength(8) |
At least this many characters — great for passwords. |
@MaxLength(50) |
At most this many characters. |
@Matches(/regex/) |
Must match a custom regular expression. |
@IsAlpha() |
Only letters — no numbers or symbols. |
@IsAlphanumeric() |
Only letters and numbers. |
@IsUppercase() |
Must be ALL CAPS. |
@IsLowercase() |
Must be all lowercase. |
@IsJSON() |
Must be a valid JSON string. |
@IsUUID() |
Must be a valid UUID — perfect for IDs. |
@IsStrongPassword() |
Enforces upper, lower, number & symbol. |
@IsDateString() |
String must represent a valid date. |
@IsMobilePhone() |
Must look like a valid phone number. |
@IsHexColor() |
Must look like a hex color, e.g. #ff0000. |
Plus ~30 more format checks with the same idea, different format: @IsIP() @IsCreditCard() @IsPostalCode() @IsJWT() @IsMacAddress() @IsISBN() @IsCurrency() @IsBase64() and others.
class SignupDto {
@IsEmail()
email: string;
@MinLength(8)
@IsStrongPassword()
password: string;
@Length(3, 30)
username: string;
}
Checks about the array as a whole — how many items, whether they repeat, and what it must contain.
| Decorator | What it does |
|---|---|
@ArrayNotEmpty() |
Array must have at least 1 item. |
@ArrayMinSize(2) |
Must contain at least this many items. |
@ArrayMaxSize(10) |
Must contain at most this many items. |
@ArrayUnique() |
No duplicate values allowed in the array. |
@ArrayContains(['admin']) |
The array must include all of these specific values. |
Beginner tip: To validate every item inside an array (not just the array itself), add the option
{ each: true }to a type or format decorator, e.g.@IsEmail({}, { each: true })on a list of emails.
For once your DTOs get more complex — validating objects inside objects, and rules that only apply sometimes.
| Decorator | What it does |
|---|---|
@ValidateNested() |
Validates an object property that is itself a class with its own decorators. |
@ValidateIf(o => cond) |
Only runs validation on this field when the condition is true. |
@ValidatePromise() |
Validates the resolved value of a Promise property. |
class AddressDto {
@IsString()
city: string;
}
class UserDto {
@ValidateNested()
@Type(() => AddressDto) // needs class-transformer
address: AddressDto;
}
When nothing built-in fits — write your own rule, or fine-tune any decorator with shared options.
| Decorator | What it does |
|---|---|
@Validate(MyConstraint) |
Use your own class implementing ValidatorConstraintInterface. |
@ValidateBy({...}) |
Define custom validation logic inline, without a separate class. |
@Allow() |
Whitelists a field so it isn't stripped when whitelist: true is set. |
{ message: '...' } — custom error text{ each: true } — validate every array item{ groups: [...] } — only run for certain validation groups{ always: true } — always run even if excluded by groups{ context: {...} } — attach metadata for error reporting@IsString({ message: 'Name must be text' })
@IsString({ each: true }) // for string[] fields
You can put many decorators on one field. Here's how they actually interact.
Decorators run top to bottom, in the order you write them (not JS's usual bottom-up decorator-application order — class-validator records them in declaration order and runs them that way).
class SignupDto {
@IsString() // runs 1st
@Length(3, 30) // runs 2nd
@Matches(/^[a-z]+$/) // runs 3rd
username: string;
}
All decorators on a field run and all failures are collected, even if an earlier one already failed. @Length() still runs even if @IsString() failed. To stop at the first failing decorator on a field, use:
@IsString()
@Length(3, 30)
username: string;
// in the controller / validate() call:
validate(dto, { stopAtFirstError: true });
These two are the exception to "all decorators run" — they can skip everything else on the field:
| Decorator | Effect |
|---|---|
@IsOptional() |
If value is undefined/null, every other decorator on this field is skipped — regardless of where @IsOptional() sits in the stack. |
@IsDefined() |
Always runs, even under @IsOptional() — the one check optional-ness can't silence. |
@IsOptional() / @IsNotEmpty() / @IsDefined() first. Decide "does this field even need checking" before anything else runs.@IsString(), @IsNumber(), @IsEnum(), etc. Confirms the shape before format/range checks try to read it.@IsEmail(), @Length(), @Min()/@Max(), @Matches(), etc. These assume the type check already passed.@ValidateIf(), custom @Validate(). These often depend on other fields or business rules, so they read best last.class SignupDto {
@IsOptional() // 1. presence
@IsString() // 2. type
@Length(3, 30) // 3. format
nickname?: string;
}
Beginner tip: Ordering doesn't change whether a decorator runs (they all run regardless of position, except the two short-circuiters above) — it only changes the order errors appear in the result array and, more importantly, makes the code read as a hierarchy: presence → type → format. Write them in that order even though
class-validatordoesn't strictly require it.
The six decorators you'll use in almost every DTO:
@IsString() / @IsNumber() / @IsEnum()@IsNotEmpty() / @IsOptional()@IsEmail() · @Length() · @Min() / @Max()Master those six first — everything else on this list is a specialized version of the same idea: check the type, then check the shape.