Preventing Mass Assignment and Type Mismatches with NestJS Global ValidationPipe
Learn how to configure NestJS ValidationPipe to prevent mass assignment vulnerabilities and ensure strict type safety using class-validator and class-transformer.
05 Jan 2026, 12:50 UTC

The Problem: Silent Data Corruption and Type Errors
In many REST APIs, developers rely on TypeScript interfaces for request bodies. However, TypeScript types are erased at runtime. If a client sends a payload containing unexpected fields or a string where a number is expected, the application may process this data blindly, leading to "mass assignment" vulnerabilities (where users update fields they shouldn't) or runtime crashes due to type mismatches in business logic.
The solution is to implement a global ValidationPipe combined with class-validator and class-transformer. This ensures that every request is validated against a concrete class definition before it ever reaches your controller logic, returning a 400 Bad Request automatically if the data is malformed.
Implementing a Strict Validation Strategy
To secure your API, you must configure the ValidationPipe in the bootstrap file (usually main.ts). A "strict" configuration prevents unknown properties from entering your system and ensures that primitive types (like IDs in query strings) are converted from strings to their intended types.
// 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({
// Strip properties that do not have any decorators in the DTO
whitelist: true,
// Throw an error if non-whitelisted properties are present
forbidNonWhitelisted: true,
// Automatically transform payloads to be objects typed according to their DTO classes
transform: true,
// Allow implicit conversion of primitives (e.g., string '10' to number 10)
transformOptions: { enableImplicitConversion: true },
}));
await app.listen(3000);
}
bootstrap();
Defining the Data Transfer Object (DTO)
For the pipe to work, you must use classes (not interfaces) and apply decorators from class-validator. This provides the runtime metadata that NestJS needs to perform the checks.
// create-user.dto.ts
import { IsString, IsEmail, IsInt, MinLength } from 'class-validator';
export class CreateUserDto {
@IsString()
@MinLength(3)
username: string;
@IsEmail()
email: string;
@IsInt()
age: number;
}
How the Mechanism Works
When a request hits a controller method using CreateUserDto, the following sequence occurs:
- Transformation:
class-transformerconverts the plain JSON object into an actual instance of theCreateUserDtoclass. - Implicit Conversion: If
enableImplicitConversionis true, the pipe looks at the TypeScript type (e.g.,number) and attempts to cast the incoming string from the URL or body into that type. - Validation:
class-validatorscans the decorators. Ifemailis missing or invalid, it triggers a validation error. - Whitelisting: If the client sends
{ "username": "dev", "email": "[contact removed]", "age": 25, "isAdmin": true }, butisAdminis not in the DTO,whitelist: trueremoves it. IfforbidNonWhitelisted: trueis also set, the request is rejected with a 400 error instead of silently stripping the field.
Limitations and Common Pitfalls
Nested Object Validation
The ValidationPipe does not automatically validate nested objects or arrays. If your DTO contains another class, you must use the @Type decorator from class-transformer to tell Nest how to instantiate the child object.
Incorrect: @IsObject() address: AddressDto; (This only checks if it is an object, not if the properties inside AddressDto are valid).
Correct: @ValidateNested() @Type(() => AddressDto) address: AddressDto;
Internal Service Calls
The ValidationPipe only operates on the HTTP layer (incoming requests). If you call a service method from another service and pass a plain object, no validation occurs. You must either manually validate using validate() from class-validator or ensure data is sanitized before reaching the service layer.
The "Silent Strip" Risk
Using whitelist: true without forbidNonWhitelisted: true can hide client-side bugs. A frontend developer might be sending a field with a typo (e.g., user_name instead of username). The API will strip the typoed field and process the request, potentially resulting in null values in your database without any error being thrown to the client.
Verification Checklist
| Test Scenario | Expected Result | Verification Method |
|---|---|---|
| Unknown Field | 400 Bad Request |
Send a JSON payload with a property not defined in the DTO. |
| Query Param Type | Correct Type (Number) | Send ?age=25; log typeof dto.age in the controller. |
| Instance Check | true |
Run console.log(dto instanceof CreateUserDto) in the handler. |
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.