Skip to content

Instantly share code, notes, and snippets.

@Setitch
Created November 24, 2022 13:42
Show Gist options
  • Save Setitch/e46baa1d608965da156525aa40359a22 to your computer and use it in GitHub Desktop.
Save Setitch/e46baa1d608965da156525aa40359a22 to your computer and use it in GitHub Desktop.
BadRequest Interceptor for NodeJS to flatten (make easy script readable) validation errors from class-validator
import { BadRequestException, CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { ValidationError } from 'class-validator';
import { catchError, Observable, throwError } from 'rxjs';
@Injectable()
export class BadRequestInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next
.handle()
.pipe(
catchError(err => {
if (err instanceof BadRequestException) {
const message = ((err.getResponse() as any).message);
if (message instanceof Array) {
const flat: any = this.flattenErrors((message as ValidationError[]).filter(m => m instanceof ValidationError));
return throwError(new BadRequestException(flat));
// return throwError(new BadRequestException(validations.map(v => v)));
}
}
return throwError(err);
}),
);
}
flattenErrors(validations: ValidationError[], previousName: string[] = []) {
const ret: Record<string, any> = {};
// console.log(validations);
validations.forEach((v) => {
if (!ret.hasOwnProperty(v.property)) ret[v.property] = [];
if (Object.values(v.constraints || {}).length !== 0) { // hide empty fields that are parents etc
ret[[...previousName, v.property].join('.')] = Object.values(v.constraints || {});
}
// console.log('o', previousName, [...previousName, v.property].join('.'), Object.values(v.constraints || {}))
if (v.children && v.children.length > 0) {
const r = this.flattenErrors(v.children, [...previousName, v.property]);
for (const prop in r) {
if (!r.hasOwnProperty(prop)) continue;
if (r[prop] instanceof Array && r[prop].length === 0) continue; // hide empty fields that are parents etc
ret[prop] = (ret[prop]) ? [...ret[prop], ...r[prop]] : r[prop];
// console.log('x', prop, ret[prop] );
}
}
if (ret[v.property].length===0) {
delete ret[v.property];
}
}, {} as any);
return ret;
}
}
@Setitch
Copy link
Author

Setitch commented Nov 24, 2022

For this to work one need to create file for new validation pipe that will override original one a bit

import { ValidationError, ValidationPipe as ValidationPipeOrigin } from '@nestjs/common';

export class ValidationPipe extends ValidationPipeOrigin {
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore we ignore the error (return type not compatible) as we want to change it in main
  protected flattenValidationErrors(validationErrors: ValidationError[]): ValidationError[] {
    return validationErrors;
  }
}

and use it as global validation pipe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment