typescript 61 lines · 10 steps

A custom validation pipe in NestJS

A NestJS pipe that strips undefined values before transforming and validating incoming DTOs.

Explained by highlit
1import {
2 Injectable,
3 PipeTransform,
4 ArgumentMetadata,
5 BadRequestException,
6} from '@nestjs/common';
7import { plainToInstance } from 'class-transformer';
8import { validate } from 'class-validator';
9 
10@Injectable()
11export class StripUndefinedValidationPipe implements PipeTransform {
12 async transform(value: unknown, { metatype }: ArgumentMetadata) {
13 if (!metatype || !this.shouldValidate(metatype)) {
14 return value;
15 }
16 
17 const pruned = this.stripUndefined(value);
18 const instance = plainToInstance(metatype, pruned, {
19 exposeUnsetFields: false,
20 });
21 
22 const errors = await validate(instance as object, {
23 whitelist: true,
24 forbidNonWhitelisted: true,
25 skipMissingProperties: true,
26 });
27 
28 if (errors.length > 0) {
29 throw new BadRequestException(
30 errors.flatMap((e) => Object.values(e.constraints ?? {})),
31 );
32 }
33 
34 return instance;
35 }
36 
37 private shouldValidate(metatype: Function): boolean {
38 const primitives: Function[] = [String, Boolean, Number, Array, Object];
39 return !primitives.includes(metatype);
40 }
41 
42 private stripUndefined(input: unknown): unknown {
43 if (Array.isArray(input)) {
44 return input.map((item) => this.stripUndefined(item));
45 }
46 
47 if (input !== null && typeof input === 'object') {
48 return Object.entries(input as Record<string, unknown>).reduce(
49 (acc, [key, val]) => {
50 if (val !== undefined) {
51 acc[key] = this.stripUndefined(val);
52 }
53 return acc;
54 },
55 {} as Record<string, unknown>,
56 );
57 }
58 
59 return input;
60 }
61}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Implementing PipeTransform lets you inject custom sanitization logic into NestJS's request lifecycle before controllers run.
  2. 2Pruning undefined keys before validation keeps whitelist rules from misfiring on properties that were never really sent.
  3. 3Recursion handles nested objects and arrays uniformly so the same cleaning rule applies at every depth.

Related explainers

Share this explainer

Here's the card — post it anywhere.

A custom validation pipe in NestJS — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code