Skip to content

Instantly share code, notes, and snippets.

@josephdpurcell
Last active June 18, 2025 06:36
Show Gist options
  • Select an option

  • Save josephdpurcell/9af97c36148673de596ecaa7e5eb6a0a to your computer and use it in GitHub Desktop.

Select an option

Save josephdpurcell/9af97c36148673de596ecaa7e5eb6a0a to your computer and use it in GitHub Desktop.

Revisions

  1. josephdpurcell revised this gist Nov 19, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -39,7 +39,7 @@ Then, you need to build another custom decorator that composes that with a valid
    ```ts
    export const ValidBody = () =>
    RawBody(
    new MyValidationPipe({
    new ValidationPipe({
    validateCustomDecorators: true,
    whitelist: true,
    forbidNonWhitelisted: true,
    @@ -62,7 +62,7 @@ Here is an example of the decorator:
    ```ts
    export const ValidBody = () =>
    Body(
    new MyValidationPipe({
    new ValidationPipe({
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
  2. josephdpurcell revised this gist Nov 19, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -21,7 +21,7 @@ https://github.com/nestjs/nest/blob/master/packages/common/pipes/validation.pipe
    # Are there alternative approaches?


    ## Alternate 1: Override global ValidationPipe
    ## Alternate 1: Use @ValidBody with custom overrides to the global ValidationPipe

    You could just change the behavior of the global validation pipe. This is a bit tricky and it only works on the param decorator level. First, you need a custom decorator:

  3. josephdpurcell revised this gist Nov 19, 2021. 1 changed file with 56 additions and 3 deletions.
    59 changes: 56 additions & 3 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,8 @@

    This is an example of how to ignore a global validation pipe for a specific parameter, e.g. a request body. In fact, this example just shows a request body but you could apply this principle to other decorators.

    This approach assumes `validateCustomDecorators: false` in the global validation pipe. If `validateCustomDecorators` is true in the global pipe I think you're out of luck. If that is your situation, consider refactoring so that `validateCustomDecorators` is false in the global pipe and then have each custom decorator add validation if it needs it.

    # How does this work?

    The NestJS `ValidationPipe` does not validate custom decorators. So, in this above example we just make a `@RawBody()` param decorator, and NestJS will skip validating it.
    @@ -18,10 +20,61 @@ https://github.com/nestjs/nest/blob/master/packages/common/pipes/validation.pipe

    # Are there alternative approaches?

    I haven't thoroughly researched this, but here are a few that come to mind:

    * Instead of applying a global validator, write a custom `@Body` decorator (e.g. `@ValidBody`) and have that decorator just use a validation pipe; see https://stackoverflow.com/a/56748392/990642
    * Write your own custom `ValidationPipe` class, use that globally, and do some checking logic there?
    ## Alternate 1: Override global ValidationPipe

    You could just change the behavior of the global validation pipe. This is a bit tricky and it only works on the param decorator level. First, you need a custom decorator:

    ```ts
    export const RawBody = createParamDecorator(
    (data: unknown, ctx: ExecutionContext): any => {
    const request = ctx.switchToHttp().getRequest();
    return request.body;
    }
    );
    ```

    Then, you need to build another custom decorator that composes that with a validation pipe like so:

    ```ts
    export const ValidBody = () =>
    RawBody(
    new MyValidationPipe({
    validateCustomDecorators: true,
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
    })
    );
    ```

    Note the use of `validateCustomDecorators: true` here! This is important. The validation pipe will not work unless this is true here. (But, remember, `validateCustomDecorators` should be false in the global validation pipe.)

    Then, use `@ValidBody` instead of `@Body` wherever you want to override the global validation pipe with your custom validation pipe rules. This approach will work even if there is a global validation pipe.


    ## Alternate 2: Use @ValidBody instead of global ValidationPipe

    Instead of applying a global validator, write a custom `@Body` decorator (e.g. `@ValidBody`) and have that decorator just use a validation pipe; see https://stackoverflow.com/a/56748392/990642. Note: I think `validateCustomDecorators: true` would need to be enabled.

    Here is an example of the decorator:

    ```ts
    export const ValidBody = () =>
    Body(
    new MyValidationPipe({
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
    })
    );
    ```

    Then, use `@ValidBody` instead of `@Body`. This approach does not work if there is a global validation pipe.

    ## Alternate 3: Custom ValidationPipe

    Write your own custom `ValidationPipe` class, use that globally, and do some checking logic there?

    # Links

  4. josephdpurcell revised this gist Nov 19, 2021. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,7 @@
    # What is this?

    This is an example of how to ignore a global validation pipe for a specific parameter, e.g. a request body. In fact, this example just shows a request body but you could apply this principle to other decorators.

    # How does this work?

    The NestJS `ValidationPipe` does not validate custom decorators. So, in this above example we just make a `@RawBody()` param decorator, and NestJS will skip validating it.
  5. josephdpurcell revised this gist Nov 19, 2021. 1 changed file with 7 additions and 0 deletions.
    7 changes: 7 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -12,6 +12,13 @@ Here is where it returns if validation shouldn't apply:

    https://github.com/nestjs/nest/blob/master/packages/common/pipes/validation.pipe.ts#L102-L106

    # Are there alternative approaches?

    I haven't thoroughly researched this, but here are a few that come to mind:

    * Instead of applying a global validator, write a custom `@Body` decorator (e.g. `@ValidBody`) and have that decorator just use a validation pipe; see https://stackoverflow.com/a/56748392/990642
    * Write your own custom `ValidationPipe` class, use that globally, and do some checking logic there?

    # Links

    Here are some links that talk about this issue and some documentation links that are relevant:
  6. josephdpurcell renamed this gist Nov 19, 2021. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  7. josephdpurcell created this gist Nov 19, 2021.
    26 changes: 26 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,26 @@
    # How does this work?

    The NestJS `ValidationPipe` does not validate custom decorators. So, in this above example we just make a `@RawBody()` param decorator, and NestJS will skip validating it.

    Take a look at NestJS' `ValidationPipe` class at the relevant points:

    Here is where it checks to see if the decorator type is 'custom':

    https://github.com/nestjs/nest/blob/master/packages/common/pipes/validation.pipe.ts#L164-L166

    Here is where it returns if validation shouldn't apply:

    https://github.com/nestjs/nest/blob/master/packages/common/pipes/validation.pipe.ts#L102-L106

    # Links

    Here are some links that talk about this issue and some documentation links that are relevant:

    * https://docs.nestjs.com/techniques/validation
    * https://docs.nestjs.com/interceptors
    * https://github.com/nestjs/nest/issues/2390
    * https://github.com/nestjs/nest/pull/3354
    * https://stackoverflow.com/questions/58751743/disable-validation-in-nestjs-param-decorator?rq=1
    * https://stackoverflow.com/questions/54471331/how-to-ignore-an-interceptor-for-a-particular-route-in-nestjs
    * https://stackoverflow.com/questions/56273543/how-to-override-global-validationpipe-in-controller
    * https://stackoverflow.com/questions/56583609/how-can-we-override-the-global-validation-pipe-in-nestjs
    11 changes: 11 additions & 0 deletions main.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    import { ValidationPipe } from '@nestjs/common';

    // ...

    app.useGlobalPipes(new ValidationPipe({
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
    }));

    // ...
    13 changes: 13 additions & 0 deletions my-controller.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    import { Body, Controller, Post } from '@nestjs/common';
    import { RawBody } from './raw-body.ts';
    import { MyDto } from './my.dto';

    @Controller()
    export class MyController {
    @Post()
    async post(
    @RawBody() params: MyDto
    ): Promise<void> {
    // params will be a raw un-typed object with no validation having been run on it!
    }
    }
    6 changes: 6 additions & 0 deletions my.dto
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    import { IsNotEmpty } from "class-validator";

    export class MyDto {
    @IsNotEmpty()
    field: string;
    }
    8 changes: 8 additions & 0 deletions raw-body.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,8 @@
    import { createParamDecorator, ExecutionContext } from '@nestjs/common';

    export const RawBody = createParamDecorator(
    (data: unknown, ctx: ExecutionContext): any => {
    const request = ctx.switchToHttp().getRequest();
    return request.body;
    }
    );