#[dto] proc-macro that turns a plain Rust struct into a fully-typed, validated Data Transfer Object. It derives Deserialize, Serialize, Validate (from the validator crate), and NestDto — and it enables a set of NestJS-style field attributes (#[IsEmail], #[Length], etc.) so your validation intent is visible at the declaration site rather than buried in implementation code.
Defining a DTO
Annotate a struct with#[dto] and add validation attributes to each field:
#[dto] expands to a struct with #[serde(deny_unknown_fields)] by default. Any JSON body that contains a key not declared on the struct returns a 400 Bad Request before your handler runs.
#[IsString] is a readability marker only — Rust’s type system already enforces that a String field is a string. It compiles to no validation code.Available validation attributes
| Attribute | Equivalent validator rule | Notes |
|---|---|---|
#[IsEmail] | validate(email) | |
#[IsUrl] | validate(url) | |
#[IsUUID] | validate(uuid) | |
#[IsString] | (no-op) | Readability marker |
#[IsBoolean] | (no-op) | Readability marker |
#[IsNotEmpty] | validate(length(min = 1)) | |
#[IsPositive] | validate(range(min = 1)) | |
#[IsNegative] | validate(range(max = -1)) | |
#[Min(n)] | validate(range(min = n)) | |
#[Max(n)] | validate(range(max = n)) | |
#[MinLength(n)] | validate(length(min = n)) | |
#[MaxLength(n)] | validate(length(max = n)) | |
#[Length(min = m, max = n)] | validate(length(min = m, max = n)) | |
#[ValidateNested] | validate(nested) | Recurse into a nested DTO field |
#[Matches(REGEX)] | validate(regex = REGEX) | |
#[Contains(pat)] | validate(contains(pat)) | |
#[IsOptional] | (no-op — use Option<T>) | Stripped at compile time |
validator attributes directly: #[validate(range(min = 0))] works on any #[dto] field.
Using ValidatedBody in a controller
PairValidatedBody<T> with your DTO type as an Axum extractor. nestrs validates the deserialized value before your handler body runs and returns 422 Unprocessable Entity on failure:
ValidatedPath and ValidatedQuery
The same pattern applies to path parameters and query strings:Nested DTOs with ValidateNested
Use#[ValidateNested] on a field whose type is itself a #[dto] struct to trigger recursive validation:
Using ValidationPipe explicitly
ValidatedBody, ValidatedPath, and ValidatedQuery run validation inline as part of extraction. If you prefer the NestJS #[use_pipes(ValidationPipe)] style you can use it with #[param::body] / #[param::query] / #[param::param]:
422 response shape when validation fails.
Allowing unknown fields
By default#[dto] adds #[serde(deny_unknown_fields)] so clients cannot send undocumented keys. To opt out:
Error response shape
A failed validation returns422 Unprocessable Entity with a JSON body:
Full example: DTO + controller + module
Full example: DTO + controller + module