question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Global validation pipe overriden in whole Resolver if overriden in single method

See original GitHub issue

Bug Report

Current behavior

I have enabled validation and transformation globally via

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

This works just fine.

Now, I wanted to validate a simple string property without having to create a class, so I created a custom validation pipe IsNotEmptyStringPipe and used it like this inside a GraphQL mutation:

@Mutation()
async mergeGroups(
  @Args('name', IsNotEmptyStringPipe) name: string,
  @Args('groupIds') groupIds: string[]
): Promise<Group> {
  return this.graphqlService.mergeGroups({ name, groupIds });
}

This also works just fine.

BUT: After some debugging, I noticed that this causes the globally enabled validation pipe to disappear on the other methods of the same resolver.

This means in this mutation:

@Mutation()
async updateGroup(
  @Args('id') id: string,
  @Args('group') group: UpdateGroupInput
): Promise<Group> {
  return this.graphqlService.updateGroup({ id, group });
}

group is no longer transformed to a UpdateGroupInput instance as long as IsNotEmptyStringPipe exists in the other method.

Input Code

main.ts

// App setup 
// ...
app.useGlobalPipes(new ValidationPipe({ transform: true }));
// ...

group.resolver.ts

@Resolver('Group')
export class GroupResolver {
  constructor(private graphqlService: GraphqlService) {}

  @Mutation()
  async updateGroup(
    @Args('id') id: string,
    @Args('group') group: UpdateGroupInput
  ): Promise<Group> {
    return this.graphqlService.updateGroup({ id, group });
  }

  @Mutation()
  async mergeGroups(
    @Args('name', IsNotEmptyStringPipe) name: string,
    @Args('groupIds') groupIds: string[]
  ): Promise<Group> {
    return this.graphqlService.mergeGroups({ name, groupIds });
  }
}

UpdateGroupInput.dto.ts

export class UpdateGroupInput {
  @IsNotEmptyString()
  @IsOptional()
  name?: string;
  @Transform(({ value }) => (value.trim() === '' ? null : value.trim()))
  @IsOptional()
  country?: string;
  @Transform(({ value }) => (value.trim() === '' ? null : value.trim()))
  @IsOptional()
  contract?: string;
  @Transform(({ value }) => (value.trim() === '' ? null : value.trim()))
  @IsOptional()
  website?: string;
}

validation.ts

import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
import {
  ValidationOptions,
  registerDecorator,
  isNotEmpty,
  ValidationArguments,
  isString,
} from 'class-validator';

export function IsNotEmptyString(validationOptions?: ValidationOptions) {
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
  return (object: unknown, propertyName: string) => {
    registerDecorator({
      name: 'isNotEmptyString',
      target: object.constructor,
      propertyName,
      options: validationOptions,
      validator: {
        validate: (value: any): boolean =>
          isString(value) && isNotEmpty(value.trim()),
        defaultMessage: (validationArguments?: ValidationArguments): string =>
          `${validationArguments.property} should be a non-empty string`,
      },
    });
  };
}

@Injectable()
export class IsNotEmptyStringPipe implements PipeTransform {
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
  transform(value: any, metadata: ArgumentMetadata): string {
    if (!(isString(value) && isNotEmpty(value.trim()))) {
      throw Error(`${metadata.data} should be a non-empty string`);
    }
    return value;
  }
}

Expected behavior

Overriding a globally enabled validation pipe in a single method should not cause the globally enabled validation pipe to disappear in other methods. This is very confusing behaviour and took me a whole day to debug why my input is no longer transformed.

Possible Solution

Environment


Nest version: 7.6.0
Node version: v16.2.0

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:9 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
ekzyiscommented, Jun 22, 2021

Okay, my minimum reproduction repo has concluded that this indeed works. So I can use

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

and a custom pipe on a method without the validation disappear on the other method …

Mhh, have to check what is different in my code …

0reactions
jmcdo29commented, Jun 23, 2021

Okay, that’s one of the common culprits. Like Kamil said, if you want to discuss this further, I’ll be happy to on our Discord. Usually quicker and easier responses there in my opinion as well

Read more comments on GitHub >

github_iconTop Results From Across the Web

How can we override the global validation pipe in NestJs?
Overriding the global validation pipe is tricky. The solution that worked for me was to override it for a specific param decorator.
Read more >
Pipes | NestJS - A progressive Node.js framework
This is possible because the value returned from the transform function completely overrides the previous value of the argument. When is this useful?...
Read more >
ASP.NET Core — FluentValidation documentation
This is the most straightforward and reliable approach. With automatic validation, FluentValidation plugs into the validation pipeline that's part of ASP.
Read more >
Extending with Shared Libraries - Jenkins
Manage Jenkins » Configure System » Global Pipeline Libraries as many libraries ... Note that the library step may not be used to...
Read more >
Jenkins Pipeline Environment Variables - The Definitive Guide
VAR = "value" assignment can override only environment variables ... If you don't do it that way, the pipeline syntax validation fails ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found