This episode covers validation and error handling in NestJS: validation with class-validator and class-transformer, a global validation pipe, custom pipes, exception filters, and a consistent error response structure.

External input can't be trusted — validation is mandatory before data reaches the business layer. On the other side, errors must be handled consistently so application clients can deal with failures properly.
Episode 7 covers both sides: validation with class-validator and class-transformer, and exception handling with pipes, filters, and a standard error response structure.
npm install class-validator class-transformerBoth are the standard libraries for validation and object transformation in the NestJS ecosystem.
A Data Transfer Object (DTO) defines the shape of the data and its validation rules:
import { IsEmail, IsString, MinLength } from "class-validator";
export class CreateUserDto {
@IsString()
@MinLength(3)
name: string;
@IsEmail()
email: string;
}The @IsString, @MinLength, and @IsEmail decorators are used by class-validator to validate properties when the object is converted.
@Controller("users")
export class UsersController {
@Post()
create(@Body() createUserDto: CreateUserDto): string {
return `User ${createUserDto.name} dibuat`;
}
}Without a validation pipe, the DTO only acts as a TypeScript type. To enable validation, we need the ValidationPipe.
Register ValidationPipe as a global pipe in main.ts:
import { ValidationPipe } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
async function bootstrap(): Promise<void> {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
await app.listen(3000);
}
void bootstrap();With whitelist: true, properties not present in the DTO are stripped out. forbidNonWhitelisted throws an error if foreign properties exist. transform: true turns the payload into a class instance — making the validation decorators work.
For custom validation, build your own pipe:
import { ArgumentMetadata, Injectable, PipeTransform, BadRequestException } from "@nestjs/common";
@Injectable()
export class ParseIdPipe implements PipeTransform<string, number> {
transform(value: string, metadata: ArgumentMetadata): number {
const parsed = parseInt(value, 10);
if (Number.isNaN(parsed)) {
throw new BadRequestException("ID harus berupa angka");
}
return parsed;
}
}A pipe is used by adding it to a parameter or using @UsePipes.
NestJS provides HttpException and its subclasses like BadRequestException, NotFoundException, and ConflictException:
import { NotFoundException } from "@nestjs/common";
@Get(":id")
findOne(@Param("id") id: string): string {
const user = this.usersService.findOne(id);
if (!user) {
throw new NotFoundException(`User ${id} tidak ditemukan`);
}
return user;
}Built-in exceptions automatically produce a response with the correct status code.
For a consistent error format, create your own filter:
import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from "@nestjs/common";
import { Response } from "express";
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost): void {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception.getStatus();
response.status(status).json({
statusCode: status,
message: exception.message,
timestamp: new Date().toISOString(),
});
}
}Register this filter globally or per-controller using @UseFilters.
With the filter above, all errors follow the same structure. A consistent structure makes it easy for the frontend to handle errors and simplifies debugging in production:
{
"statusCode": 404,
"message": "User 99 tidak ditemukan",
"timestamp": "2026-08-10T07:00:00.000Z"
}Episode 7 polishes your API from both the security and developer experience sides: DTO validation with class-validator, a global ValidationPipe, custom pipes, exception filters, and consistent error responses.
Key takeaways:
whitelist and transform make the ValidationPipe work optimally.@Body type in controllers.PipeTransform.HttpException and its subclasses produce automatic status codes.In the next episode 8 we'll discuss configuration and environment management — the @nestjs/config module, configuration modules, per-environment configuration, validation schemas, externalized configuration, and secret management in development and production.