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,
transformOptions: {
enableImplicitConversion: true,
},
errorHttpStatusCode: 422,
}),
);
await app.listen(3000);
}
bootstrap();
import { Type, Transform } from 'class-transformer';
import {
IsEmail,
IsInt,
IsOptional,
IsString,
Length,
Min,
ValidateNested,
} from 'class-validator';
export class AddressDto {
@IsString()
@Length(1, 120)
street: string;
@IsString()
@Length(2, 2)
@Transform(({ value }) => String(value).toUpperCase())
countryCode: string;
}
export class CreateUserDto {
@IsEmail()
@Transform(({ value }) => String(value).trim().toLowerCase())
email: string;
@IsString()
@Length(2, 60)
name: string;
@IsInt()
@Min(18)
age: number;
@IsOptional()
@ValidateNested()
@Type(() => AddressDto)
address?: AddressDto;
}
import { Body, Controller, Get, Param, ParseIntPipe, Post } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './create-user.dto';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(@Body() dto: CreateUserDto) {
// dto is already validated and transformed into a class instance
return this.usersService.create(dto);
}
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.usersService.findOne(id);
}
}
This snippet shows the standard NestJS approach to validating and shaping incoming HTTP payloads at the framework boundary, so controllers only ever see well-typed, sanitized data. The core idea is that request validation should be declarative and colocated with the shape it describes — the DTO — rather than scattered through imperative checks inside controllers.
In main.ts, a single app.useGlobalPipes(new ValidationPipe(...)) registers validation for every route. The whitelist: true option strips any property not decorated in the DTO, and forbidNonWhitelisted: true turns unexpected fields into a 400 instead of silently dropping them, which closes off mass-assignment style bugs. transform: true is what upgrades a plain parsed JSON object into an actual instance of the DTO class, and enableImplicitConversion lets primitive coercion (query strings to numbers, for example) happen automatically. Setting a non-default errorHttpStatusCode is a common tweak for APIs that prefer 422.
In create-user.dto.ts, the payload contract is expressed entirely through decorators. class-validator decorators like @IsEmail, @Length, @IsInt, and @Min describe the rules, while class-transformer decorators do the shaping: @Transform on email normalizes casing and trims whitespace before validation runs, and @Type(() => AddressDto) combined with @ValidateNested tells the transformer how to hydrate nested objects so the address is validated too, not treated as an opaque blob. @Expose/@Exclude semantics pair naturally with this when responses are serialized.
The order matters: transformation happens first, then validation sees the cleaned values, which is why trimming an email in @Transform prevents spurious @IsEmail failures. A subtle pitfall is that @Type is mandatory for nested DTOs — without it the nested object stays a plain Object and @ValidateNested silently passes.
In users.controller.ts, the payload arrives as a fully validated CreateUserDto instance via @Body(), with no manual parsing. Because the pipe already guaranteed the shape, create can trust its input and delegate straight to the service. This pattern trades a little decorator verbosity for controllers that stay thin, errors that are consistent across the whole app, and a validation contract that doubles as living documentation.
Related snips
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
export interface RetryOptions {
retries: number;
baseMs: number;
maxMs: number;
signal?: AbortSignal;
onRetry?: (attempt: number, delay: number, err: unknown) => void;
Exponential backoff with jitter for retries
module Api
module V1
class UsersController < BaseController
def show
user = User.includes(:profile).find(params[:id])
ETags for conditional requests and caching
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: String!
GraphQL API with Spring Boot
class SignupForm
include ActiveModel::Model
include ActiveModel::Attributes
attribute :account_name, :string
attribute :email, :string
Shallow Controller, Deep Params: Form Object Pattern
import axios, { AxiosError } from 'axios'
import { v4 as uuidv4 } from 'uuid'
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1',
timeout: 15000,
Axios API client with interceptors
Share this code
Here's the card — post it anywhere.