DTO validation profiling
Inspect DTO validations — both successful and failed — in the Validator panel.
This tutorial shows how to add the validator collector to a NestJS application. The collector is validator-agnostic; this walkthrough uses class-validator (the default), and a nestjs-zod variant is shown below.
Prerequisites
@eleven-labs/nest-profilerinstalled and configured- A validation library. This tutorial uses
class-validator+class-transformer, which are required only for the default class-validator pipe — they are not dependencies of@eleven-labs/nest-profiler-validatoritself. For the nestjs-zod variant, installnestjs-zod+zodinstead.
No separate pipe needed
ValidatorCollectorModule registers a global APP_PIPE internally. You do not need to call
app.useGlobalPipes() or configure ValidationPipe yourself — the module handles it.
Step 1 — Install the package
pnpm add @eleven-labs/nest-profiler-validator
# this tutorial uses the default class-validator pipe:
pnpm add class-validator class-transformerStep 2 — Register the module
import { Module } from '@nestjs/common';
import { ProfilerModule } from '@eleven-labs/nest-profiler';
import { ValidatorCollectorModule } from '@eleven-labs/nest-profiler-validator';
@Module({
imports: [ProfilerModule.forRoot({ isGlobal: true }), ValidatorCollectorModule.forRoot()],
})
export class AppModule {}Step 3 — Create a DTO with validation constraints
Use value imports, not type imports
Always import decorator functions from class-validator as value imports (not import type). The
decorators must be executed at runtime to register metadata. Using import type strips them at
compile time.
import { IsString, IsNotEmpty, MinLength, MaxLength, IsOptional, IsUrl } from 'class-validator';
export class CreateArticleDto {
@IsString()
@IsNotEmpty()
@MinLength(5)
@MaxLength(120)
title: string;
@IsString()
@IsNotEmpty()
@MinLength(20)
body: string;
@IsOptional()
@IsUrl()
coverImageUrl?: string;
}Step 4 — Use the DTO in a controller
import { Body, Controller, Post } from '@nestjs/common';
import { CreateArticleDto } from './dto/create-article.dto';
@Controller('articles')
export class ArticlesController {
@Post()
create(@Body() dto: CreateArticleDto) {
return { message: 'Article created', title: dto.title };
}
}Step 5 — Test it
Valid request:
curl -i -X POST http://localhost:3000/articles \
-H "Content-Type: application/json" \
-d '{"title": "My first article", "body": "This is the body of the article with enough content."}'Invalid request:
curl -i -X POST http://localhost:3000/articles \
-H "Content-Type: application/json" \
-d '{"title": "Hi", "body": "Too short"}'Open /_profiler/{token} and click the Validator tab.
For the valid request, you will see:
- Source —
ArticlesController.create - DTO class —
CreateArticleDto - Status —
valid - Violations — none
For the invalid request, you will see:
- Source —
ArticlesController.create - DTO class —
CreateArticleDto - Status —
invalid - Violations — one entry per failing property, listing the constraint names and messages
The toolbar badge shows the total count of validated DTOs, or N violations when at least one validation failed (e.g., 2 violations).
Capturing both valid and invalid validations
The collector records every validation that passes through the pipe — both successful and failed. This lets you:
- Confirm that valid payloads pass all constraints
- See exactly which constraints failed and on which properties
- Debug validation logic without adding temporary logging
Even when validation fails and NestJS returns a 400 Bad Request, the profile is still created
and the failed validation is recorded. Navigate to /_profiler to find the profile by timestamp.
Using a different validator (nestjs-zod)
The collector is validator-agnostic. To profile a nestjs-zod app instead, pass its pipe via the pipe option — class-validator is never loaded:
import { ZodValidationPipe } from 'nestjs-zod';
@Module({
imports: [
ProfilerModule.forRoot({ isGlobal: true }),
ValidatorCollectorModule.forRoot({ pipe: new ZodValidationPipe() }),
],
})
export class AppModule {}import { createZodDto } from 'nestjs-zod';
import { z } from 'zod';
const ArticleSchema = z.object({
title: z.string().min(5).max(120),
body: z.string().min(20),
coverImageUrl: z.string().url().optional(),
});
export class CreateArticleDto extends createZodDto(ArticleSchema) {}The Validator panel renders zod violations exactly the same way — one entry per failing property with its messages. A NestJS app uses a single global validation strategy, so use one validator at a time.
How it works
ValidatorCollectorModule registers ProfilerValidationPipe as a global APP_PIPE. Rather than validating itself, this pipe wraps the validation pipe you configure (a class-validator ValidationPipe by default, or any pipe you pass via pipe). After delegating, it writes an entry into the CLS profile for the current request — the DTO class name, handler source, and status. On failure it runs a chain of duck-typed extractors over the thrown error (class-validator, then zod, then a generic HttpException fallback) to normalize the violations, then re-throws the original exception. To support another validator, implement ValidationViolationExtractor and pass it via the extractors option.