Last active
March 28, 2023 08:06
-
-
Save Deathspike/faba48728ba3c3d986f10e3f63ada372 to your computer and use it in GitHub Desktop.
nestjs response validation example (using express)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as app from '.'; | |
import * as api from '@nestjs/common'; | |
@api.Controller() | |
export class TestController { | |
@api.Get() | |
@app.ResponseValidator(app.TestDto) | |
get() { | |
return new app.TestDto(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as clv from 'class-validator'; | |
export class TestDto { | |
@clv.IsString() | |
readonly name!: string; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as app from '.'; | |
import * as api from '@nestjs/common'; | |
import * as clt from 'class-transformer'; | |
export function ResponseValidator<T>(cls: api.Type<T>, options?: clt.ClassTransformOptions) { | |
return api.UseInterceptors(new app.ResponseValidatorInterceptor(cls, options)); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as api from '@nestjs/common'; | |
import * as clt from 'class-transformer'; | |
import * as clv from 'class-validator'; | |
import * as rxo from 'rxjs/operators'; | |
import express from 'express'; | |
export class ResponseValidatorInterceptor<T> implements api.NestInterceptor { | |
private readonly _cls: api.Type<T>; | |
private readonly _options?: clt.ClassTransformOptions; | |
constructor(cls: api.Type<T>, options?: clt.ClassTransformOptions) { | |
this._cls = cls; | |
this._options = options; | |
} | |
intercept(context: api.ExecutionContext, next: api.CallHandler) { | |
return next.handle().pipe(rxo.map(async (value: T) => { | |
const validationErrors = await (value instanceof this._cls | |
? clv.validate(value) | |
: clv.validate(clt.plainToClass(this._cls, value, this._options))); | |
if (validationErrors.length) { | |
const errors = flatten(validationErrors); | |
const message = 'Validation failed'; | |
const response = context.switchToHttp().getResponse<express.Response>(); | |
const statusCode = 500; | |
response.status(statusCode); | |
return {statusCode, message, errors, value}; | |
} else { | |
return value; | |
} | |
})); | |
} | |
} | |
function flatten(errors: Array<clv.ValidationError>, previousProperty?: string) { | |
const result: Array<{constraints: Record<string, string>, property: string}> = []; | |
errors.forEach(error => map(error, result, previousProperty)); | |
return result; | |
} | |
function map(error: clv.ValidationError, result: Array<{constraints: Record<string, string>, property: string}>, previousProperty?: string) { | |
const property = previousProperty ? `${previousProperty}.${error.property}` : error.property; | |
if (error.constraints) result.push(({property, constraints: error.constraints})) | |
if (error.children) result.push(...flatten(error.children, property)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This response validator is an interceptor that validates your return value, and returns a 500 status when the return value does not match your schema. It makes sure that your API cannot send incorrect data to your consumers. Use as you see fit; MIT license.