[ ] Regression
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
The transform method of global pipes is not called.
The transform method of global pipes should be called before calling a resolver method.
core.module.ts
providers: [
{
provide: APP_PIPE,
scope: Scope.REQUEST,
useClass: CheckInputPipe,
}
]
check-input.pipe.ts
@Injectable({ scope: Scope.REQUEST })
export class CheckInputPipe implements PipeTransform<any> {
constructor(@Inject(CONTEXT) private readonly context) {}
async transform(value: any, { metatype }: ArgumentMetadata) {
console.log('Transform method is called!');
}
Console log does not occur.
To test this, you can use the following repository:
https://github.com/yostools/nest-example
clone https://github.com/yostools/nest-example.git
cd nest-example
npm i
npm run test:e2e
If everything would work, the console log Current context should appear.
I would like to use a global pipe that compares the input with the rights of the current user and only passes on the inputs to which the user has authorization.
check-input.pipe.ts
import { ArgumentMetadata, BadRequestException, Inject, Injectable, PipeTransform, Scope } from '@nestjs/common';
import { CONTEXT } from '@nestjs/graphql';
import { plainToClass } from 'class-transformer';
import { validate, ValidationError } from 'class-validator';
import { checkRestricted } from '../decorators/restricted.decorator';
import { Context } from '../helpers/context.helper';
/**
* The CheckInputPipe checks the permissibility of individual properties of inputs for the resolvers
* in relation to the current user
*/
@Injectable({ scope: Scope.REQUEST })
export class CheckInputPipe implements PipeTransform<any> {
/**
* Constructor to inject context
*/
constructor(@Inject(CONTEXT) private readonly context) {}
/**
* Check input
*/
async transform(value: any, { metatype }: ArgumentMetadata) {
// Return value if it is only a basic type
if (!metatype || this.isBasicType(metatype)) {
return value;
}
// Remove restricted values if roles are missing
const { user }: any = Context.getData(this.context);
value = checkRestricted(value, user);
// Validate value
const plainValue = JSON.parse(JSON.stringify(value));
const object = plainToClass(metatype, plainValue);
const errors: ValidationError[] = await validate(object);
// Check errors
if (errors.length > 0) {
throw new BadRequestException('Validation failed');
}
// Everything is ok
return value;
}
/**
* Checks if it is a basic type
*/
private isBasicType(metatype: any): boolean {
const types = [String, Boolean, Number, Array, Object, Buffer, ArrayBuffer];
return types.includes(metatype);
}
}
Nest version: 6.5.2
For Tooling issues:
- Node version: 12.4.0
- Platform: Mac
Others:
The error has occurred since version 6.5.0.
The intercept method is also not being called in case of an Interceptor. I think for the graphql package in general the request scoped providers are not being invoked.
I'm new to nest and I've been trying to write a db transaction interceptor for my graphql mutations, but these issues with no injections are causing me problems.
Is there some sort of work around here, or another way to wrap mutations with a request scoped function that will inject services?
@lnmunhoz @chrisregnier simply use controller-scoped or method-scoped interceptors instead of global ones.
Actually I am using method scoped interceptors already and that's what's not working for me. I haven't tried the other scopes though so I'll give those a try. Thanks.
@chrisregnier could you please create a separate issue + provide a reproduction repository?
I've been trying all day to reproduce my problem in a small test case, but everything seems to be working. So I've clearly got unrelated problems in my own code. Thanks.
I'm also getting the same issue as @kaihaase. Is there any timeline for fixing this issue?
I'm having similar issue as @kaihaase and @lnmunhoz . I need global interceptor to do some manipulations for all incoming requests before and after response. On nestjs 6.5.0 Request scoped global interceptor works great for regular rest requests, but does not work for graphql requests
I just tested this w/ Nest V7 and was unable to get it to work with a request scoped Global Interceptor (via provider syntax) or w/ a request scope Global Guard. I'm able to get it to work w/ method level guards/interceptors but that's not an optimal solution. Is there any update w/ adding this feature?
For anyone coming to this issue at the moment, request scoping an interceptor or guard doesn't make much sense because they already have access to the request object. What can be done, is inject the ModuleRef class and get the request scoped instance of the class. A sample of this would look something like
@Injectable()
export class InterceptorUsingReqScopedProvider implements NestInterceptor {
constructor(private readonly modRef: ModuleRef) {}
async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<unknown>> {
const req = this.getRequest(context);
const contextId = ContextIdFactory.getByRequest(req);
const service = await modRef.resolve(ServiceName, contextId);
// do something with the service
return next.handle();
// can also work in the `.pipe` of the observable
}
getRequest(context: ExecutionContext) {
// this can have added logic to take care of REST and other contexts
const gql = GqlExecutionContext.create(context);
return gql.getContext().req;
}
}
To make sure this works, you should also make sure to set up the context properly from the GraphqlModule like so
GraphqlModule.forRoot({
context: ({ req }) => ({ req }),
// other options
})
Just a small note:
For anyone coming to this issue at the moment, request scoping an interceptor or guard doesn't make much sense because they already have access to the request object.
Request scoping an interceptor or guard may make sense in case they depend on request-scoped providers which factory functions perform some complex, async operations.
Using ModuleRef (as shown above) is a good workaround for now.
Request scoping an interceptor or guard may make sense in case they depend on request-scoped providers which factory functions perform some complex, async operations.
You're right. I think I was trying to say making these enhancers explicitly request scoped isn't necessary due to being able to use the ModuleRef class, and resolving the dependency, but didn't make the point clear.
As @jmcdo29 pointed out, it's not a big issue for interceptors or guards, because they already have access to execution context.
However, I'm also experiencing the same issue with global pipes, and they don't have that access.
It should be also noted, that not only transform method is not called, but the pipe is not constructed at all (constructor is not called).
I'm using NestJS 7.6.13. Use case for request-scoped global pipes - automatic validation of entities existence (by IDs) in databases split by tenants (SaaS).
Most helpful comment
I'm having similar issue as @kaihaase and @lnmunhoz . I need global interceptor to do some manipulations for all incoming requests before and after response. On nestjs 6.5.0 Request scoped global interceptor works great for regular rest requests, but does not work for graphql requests