How to Validate Data in NestJS with Pipes and DTOs
Now that you understand the NestJS request lifecycle, it is time to zoom in on one of the most practical parts of it:
pipes and DTOs
Because let’s be honest, one of the fastest ways to make an API weird is to accept random input and just hope for the best.
That usually ends with:
- missing fields
- wrong data types
- invalid emails
- empty strings pretending to be useful data
- debugging sessions that somehow become philosophical
This is exactly why validation matters.
And in NestJS, validation can actually feel pretty clean once you understand the pattern.
In this article, we will make it simple.
By the end, you will understand:
- what a DTO is
- what a pipe does
- what
ValidationPipeis for - how
class-validatorandclass-transformerfit in - how to validate request bodies, params, and query values
- what mistakes beginners usually make
Let’s clean up your input before your controller has to deal with it.
Why Input Validation Matters So Much
When a client sends data to your API, that data should never be treated like it is automatically correct.
Because clients can send:
- missing values
- wrong types
- bad formats
- unexpected fields
- completely broken payloads
And if your controller or service has to keep checking all of that manually, things get messy fast.
Nest’s official validation docs position ValidationPipe as the convenient built-in way to enforce rules for incoming payloads, with those rules declared on DTO classes using class-validator decorators.
That is a big deal.
Because instead of writing repetitive if statements everywhere, you can define validation rules once and let Nest enforce them consistently.
That leads to code that is:
- cleaner
- safer
- easier to reuse
- easier to maintain
Very good trade.
What Is a DTO in NestJS?
DTO stands for Data Transfer Object.
In plain English, a DTO is usually a class that describes the shape of the data your API expects or returns.
For input validation, a DTO helps you define things like:
- which fields are required
- what type each field should be
- which values are allowed
- what format is expected
Example:
export class CreateUserDto {
name: string;
email: string;
password: string;
}That is the basic idea.
But on its own, this only describes shape.
To make it actually validate incoming data, you usually add validation decorators from class-validator, which Nest’s ValidationPipe uses.
So DTOs are not just “nice structure.”
In NestJS, they often become the main place where your validation rules live.
What Is a Pipe in NestJS?
Nest’s pipes docs say pipes are mainly used for transformation and validation, and that they run just before the route handler is invoked.
That means pipes are perfect for things like:
- validating request bodies
- parsing IDs
- transforming input values
- rejecting bad data before the controller runs
This is important because it keeps your controller cleaner.
Instead of doing this in every route:
if (!body.email || typeof body.email !== 'string') {
throw new BadRequestException('Invalid email');
}you let a pipe handle that work before the request reaches your real logic.
That is much more Nest-like.
The Star of the Show: ValidationPipe
ValidationPipe is Nest’s built-in validation solution for incoming payloads, and the official docs recommend it as the standard approach for enforcing DTO-based rules. It uses class-validator under the hood.
This is the pipe most beginners should learn first.
Why?
Because it gives you a clean pattern:
- define a DTO
- add validation decorators
- apply
ValidationPipe - let Nest reject invalid input automatically
That is a very nice workflow.
How class-validator Fits In
Nest’s ValidationPipe relies on the class-validator package and its declarative validation decorators.
That means you can write DTOs like this:
import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';
export class CreateUserDto {
@IsNotEmpty()
name: string;
@IsEmail()
email: string;
@MinLength(8)
password: string;
}Now your DTO is not only describing the fields.
It is also declaring the validation rules.
That is one of the cleanest parts of the Nest validation story.
The rules live right next to the data shape.
Much easier to read than scattering checks all over controllers.
How class-transformer Fits In
Nest’s pipes docs explain that incoming request bodies are plain objects, and validation based on decorated classes works properly when the payload is treated as an instance of the decorated DTO class instead of just a plain object. That is where transformation matters. The class-transformer project describes plainToInstance as the function that turns a plain object into an instance of a class.
In simple terms:
- request body arrives as plain JSON
- DTO is a decorated class
- validation works best when Nest transforms the plain object into that DTO class shape
So:
class-validatorhandles the validation rulesclass-transformerhelps turn input into DTO class instances
They work together very naturally in Nest.
Your First Validated DTO Example
Let’s make this real.
Step 1: Install the packages
Nest’s validation guide uses class-validator and class-transformer as the standard packages for DTO validation and transformation.
npm install class-validator class-transformerStep 2: Create a DTO
import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';
export class CreateUserDto {
@IsNotEmpty()
name: string;
@IsEmail()
email: string;
@MinLength(8)
password: string;
}Step 3: Use it in a controller
import { Body, Controller, Post, UsePipes, ValidationPipe } from '@nestjs/common';
import { CreateUserDto } from './create-user.dto';
@Controller('users')
export class UsersController {
@Post()
@UsePipes(new ValidationPipe())
create(@Body() body: CreateUserDto) {
return {
message: 'User created successfully',
data: body,
};
}
}Now if the request body is missing required fields or the email is invalid, ValidationPipe can reject it before your method logic continues. That is exactly the behavior Nest documents for ValidationPipe.
That is already a huge improvement over manual validation everywhere.
Why This Feels So Much Better Than Manual Validation
Without DTOs and pipes, validation often ends up:
- repetitive
- inconsistent
- easy to forget
- annoying to test
- ugly inside controllers
With DTOs + ValidationPipe, you get:
- a single place for validation rules
- automatic enforcement
- cleaner controllers
- easier reuse across routes
- more predictable API behavior
This is one of those features that quickly feels non-optional once you get used to it.
Because nobody enjoys writing the same input checks fifty times.
Global ValidationPipe: The Setup Most Apps Want
Nest’s validation docs show ValidationPipe being applied globally so all endpoints benefit from the same validation behavior.
That usually looks like this in main.ts:
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
await app.listen(3000);
}
bootstrap();These options matter a lot:
whitelist: true
This strips properties that are not part of the DTO. Nest documents whitelist as removing validated objects’ extra properties that have no decorators.
forbidNonWhitelisted: true
Instead of quietly stripping extra properties, this throws an error when unknown properties are present. Nest documents this option as a stricter alternative to silent stripping.
transform: true
This enables transformation so incoming values can be converted to the types expected by your DTO or handler, which Nest highlights as important when validating decorated DTO classes and transforming route/query values.
For most real apps, this global setup is a very good starting point.
Validating Route Parameters with Pipes
Pipes are not only for request bodies.
They are also great for route params.
Nest’s pipes docs present built-in transformation pipes like ParseIntPipe specifically for route parameter handling.
Example:
import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return { id };
}
}If the route receives /users/abc, ParseIntPipe can reject it before your controller logic continues. Nest documents this exact style of parameter transformation pipe usage.
That is much better than accepting a broken value and hoping nothing weird happens later.
Validating Query Parameters
You can also validate query parameters using DTOs or transformation pipes.
For example:
import { IsInt, IsOptional, Min } from 'class-validator';
import { Type } from 'class-transformer';
export class ListUsersQueryDto {
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number;
}Then in the controller:
import { Controller, Get, Query } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get()
findAll(@Query() query: ListUsersQueryDto) {
return {
page: query.page ?? 1,
};
}
}This pattern depends on transformation because query params arrive as strings, and class-transformer provides decorators and transformation behavior for converting plain values to typed DTO properties.
Very useful for pagination, filters, and optional query logic.
The Most Common Validation Decorators Beginners Use
class-validator provides many decorators, and Nest’s ValidationPipe is designed to work with them.
The ones beginners use most often are:
@IsNotEmpty()@IsString()@IsEmail()@MinLength()@IsOptional()@IsInt()@Min()@Max()
Example:
import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsString, Min } from 'class-validator';
export class CreateProfileDto {
@IsString()
@IsNotEmpty()
username: string;
@IsEmail()
email: string;
@IsOptional()
@IsInt()
@Min(13)
age?: number;
}This is already enough to handle a lot of normal API input validation.
You do not need to memorize the whole decorator universe on day one.
Transforming Input Is Not the Same as Validating Input
This is one beginner confusion that shows up a lot.
Validation
Validation answers:
- Is this field present?
- Is it the right format?
- Is it long enough?
- Is it allowed?
Transformation
Transformation answers:
- Should
"42"become42? - Should plain JSON become a DTO class instance?
- Should this string become a boolean or date-like typed value?
Nest’s docs separate these concerns clearly: pipes are for both transformation and validation, and ValidationPipe can do both when configured with transformation.
So yes, they work together.
But they are not the same thing.
Common Beginner Mistakes with DTOs and Pipes
Let’s save some frustration.
1. Forgetting to install class-validator and class-transformer
ValidationPipe depends on these packages for the most common DTO validation workflow Nest documents.
2. Creating a DTO but not using ValidationPipe
A DTO by itself does not automatically enforce validation rules.
The pipe is what tells Nest to validate incoming payloads against those rules.
3. Expecting types to magically convert without transform: true
Incoming request data is plain text/JSON by default. Transformation must be enabled if you want typed conversion behavior.
4. Doing validation in the controller anyway
If your controller is still manually checking every field, the validation pattern is not doing its job.
Controllers should stay thinner than that.
5. Forgetting whitelist
Without it, clients can send extra fields you never intended to accept. Nest explicitly documents whitelist for stripping unknown properties from validated inputs.
6. Confusing DTOs with database entities
A DTO is mainly about data transfer and validation boundaries.
It is not automatically the same thing as your database model.
That separation becomes more important as your app grows.
A Good Mental Model to Remember
Here is the easiest way to remember the whole pattern:
- DTO = describes expected input shape
- class-validator = declares validation rules
- class-transformer = helps convert plain input into typed class instances
- ValidationPipe = applies the rules before the controller runs
That is the full team.
And once you get used to it, request validation becomes much more pleasant.
Which is not a sentence people usually say about validation, but here we are.
Why This Makes Your API Better
Using DTOs and pipes does more than make your code look cleaner.
It also improves your API itself.
You get:
- more predictable input handling
- fewer weird runtime bugs
- clearer validation behavior
- more consistent error responses
- cleaner boundaries between transport and business logic
Nest’s validation approach is popular for a reason.
It turns validation from random manual checking into a repeatable pattern.
That is a very good upgrade.
Final Thoughts
Validation is one of those things that beginners sometimes treat like an optional detail.
It is not.
A clean API needs clear rules about what input it accepts.
NestJS gives you a very nice way to do that with:
- DTOs
ValidationPipeclass-validatorclass-transformer
Pipes are designed for transformation and validation, and ValidationPipe is the official built-in tool Nest recommends for enforcing DTO-based rules on incoming payloads.
That means your controllers can stay focused on handling requests instead of babysitting every field in the payload.
Which is exactly the kind of structure NestJS is good at.
And now that input validation is clear, the next step is another major part of request handling:
Because once your input is clean, the next question becomes:
Should this request even be allowed to continue?
Real Interview Questions
What is a DTO in NestJS?
A DTO, or Data Transfer Object, is usually a class that defines the expected structure of input or output data. In Nest validation workflows, DTO classes commonly hold validation decorators used by ValidationPipe.
What does ValidationPipe do in NestJS?
ValidationPipe validates incoming payloads against rules declared on DTO classes and can also transform input when configured to do so.
Do I need class-validator for NestJS validation?
For the standard DTO-based validation pattern documented by Nest, yes. ValidationPipe uses class-validator decorators to enforce rules.
Why do I need class-transformer in NestJS validation?
Because incoming request bodies are plain objects, and transformation helps treat them as decorated DTO class instances. class-transformer provides utilities like plainToInstance for that conversion.
Should I use ValidationPipe globally?
In many apps, yes. Nest’s docs show global usage as a common pattern so validation behavior is applied consistently across routes.