NestJS Profiler
Packages

nest-profiler-validator

Inspect DTO validation violations in the Validator panel.

@eleven-labs/nest-profiler-validator

@eleven-labs/nest-profiler-validator captures every DTO validation result (valid or invalid) and displays it in a dedicated Validator panel, inspired by Symfony's Web Profiler validator tab.

It is validator-agnostic: instead of being tied to class-validator, it wraps any validation PipeTransform and normalizes failures through pluggable, duck-typed extractors. Built-in extractors cover class-validator, nestjs-zod, and a generic HttpException fallback.

Validator panel — DTO validation results with per-property constraint violations

Installation

pnpm add @eleven-labs/nest-profiler-validator@alpha

There is no stable release yet — install every @eleven-labs/nest-profiler* package with the @alpha dist-tag (@latest resolves to nothing).

Then install the validator you use:

# class-validator (default)
pnpm add class-validator class-transformer

# …or nestjs-zod
pnpm add nestjs-zod zod

class-validator/class-transformer are not peer dependencies — they are only required when you rely on the default class-validator pipe.

Setup

Own the validation pipe in your bootstrap with createProfilerValidationPipe(), and register the panel with ValidatorCollectorModule.forRoot(). Validation runs independently of the profiler, so the panel can be gated like every other collector while validation always runs.

With class-validator (default)

main.ts
import {
  createProfilerValidationPipe,
  createClassValidatorPipe,
} from '@eleven-labs/nest-profiler-validator';

const app = await NestFactory.create(AppModule);

app.useGlobalPipes(
  createProfilerValidationPipe(createClassValidatorPipe({ whitelist: true, transform: true })),
);

Wrap createClassValidatorPipe (rather than a bare new ValidationPipe()) so the raw ValidationError[] reaches the panel and violations show per property.

app.module.ts
import { ConditionalModule } from '@nestjs/config';
import { ValidatorCollectorModule } from '@eleven-labs/nest-profiler-validator';

const isProfilerEnabled = (env: NodeJS.ProcessEnv) => env['PROFILER_ENABLED'] === 'true';

@Module({
  imports: [ConditionalModule.registerWhen(ValidatorCollectorModule.forRoot(), isProfilerEnabled)],
})
export class AppModule {}

With nestjs-zod

Pass your own pipe; class-validator is never loaded:

main.ts
import { ZodValidationPipe } from 'nestjs-zod';

app.useGlobalPipes(createProfilerValidationPipe(new ZodValidationPipe()));

A NestJS app uses a single global validation strategy, so use one validator at a time. createProfilerValidationPipe(inner, extractors?) also accepts a custom extractor chain as its second argument.

The pipe writes outcomes to CLS; the gated panel reads them only when the profiler is on. When the profiler is off the pipe validates and records nothing (transparent pass-through).

e2e / manual bootstrap — when you boot the app yourself in tests (Test.createTestingModule(...).createNestApplication()), mirror this useGlobalPipes(...) call there too, since it lives in main.ts rather than a module.

The extractor chain ([classValidator, zod, generic]) rarely needs changing; pass a custom one as the second argument of createProfilerValidationPipe(inner, extractors).

Enabling / disabling — gate the panel with ConditionalModule.registerWhen(..., isProfilerEnabled) as shown, so it loads only when PROFILER_ENABLED is on (a top-level enabled option is also supported). Wire the core ProfilerModule once at the root — the recommended setup bundles the root-level profiler modules into a single ProfilingModule behind a ConditionalModule gate (see Enabling and disabling the profiler and the example app).

Prerequisite: value import for DTO types

For reflect-metadata to emit the DTO class constructor as parameter metadata, use a value import (not import type) on the DTO in your controllers:

products.controller.ts
// ✓ value import — emits reflect-metadata
import { CreateProductDto } from './dto/create-product.dto';

// ✗ type-only import — metadata is erased, metatype shows as 'Function'
import type { CreateProductDto } from './dto/create-product.dto';

What it captures

For each @Body(), @Query(), or @Param() parameter using a DTO class:

FieldDescription
sourcebody, query, param, or custom
dtoClassDTO class name (e.g., CreateProductDto)
statusvalid or invalid
violationCountTotal number of constraint violations
violationsPer-property breakdown with constraint names and messages

Each violation entry includes:

  • property — the property path that failed (nested properties use dot notation)
  • value — the rejected value (when available)
  • constraints — map of constraint name → message (e.g., { isNotEmpty: "name should not be empty" })

How it works

ProfilerValidationPipe implements PipeTransform and wraps an inner pipe:

  1. On transform(), it delegates to the inner pipe. On success it records a valid entry.
  2. On failure it runs the configured extractors over the thrown error, records an invalid entry with the normalized violations, then re-throws the original exception.

Extractors are tried in order; the first to recognize the error wins:

  • class-validatorcreateClassValidatorPipe() attaches the raw ValidationError[] to the thrown exception (under a private symbol) so the full property/constraint tree is recovered.
  • nestjs-zod / zod — reads ZodError.issues (via getZodError() or a bare ZodError).
  • generic — any HttpException exposing a message string/array (the universal fallback).

Reading the active profile uses CLS, so capture is concurrent-safe across requests.

Custom extractors

To support another validator, implement ValidationViolationExtractor and pass it via extractors:

import type { ValidationViolationExtractor } from '@eleven-labs/nest-profiler-validator';

const myExtractor: ValidationViolationExtractor = {
  extract({ error }) {
    // return ViolationEntry[] if recognized, otherwise null to defer to the next extractor
    return null;
  },
};

app.useGlobalPipes(createProfilerValidationPipe(myPipe, [myExtractor]));

Toolbar badge

  • All valid: number of validated DTOs (e.g., 1)
  • With violations: total violation count (e.g., 3 violations)
Powered & maintained by

On this page