Search code examples
javascriptmodulenestjsgrpcinterceptor

Run NestJs service function in GRPC interceptors definition


i want run a nestjs service funtion in grpc interceptors definitions, grpc service client register file, my service module class where i register the grpc service to invoke

@Module({
    imports: [
        ConfigModule,
        ClientsModule.registerAsync([
            {
                name: 'tokenvalidator',
                imports: [ConfigModule, ClsModule],

                useFactory: async (configService: ConfigService) => ({
                    transport: Transport.GRPC,
                    options: {
                        package: 'br',
                        protoPath: join(__dirname, '../../protos/tokenvalidator.proto'),
                        url: configService.get('GRPC_TOKEN_VALIDATOR_HOST'),
                        channelOptions: {
                            interceptors: [(options, nextCall) => {
                                new GRPCInterceptor().interceptGrpcCall(options, nextCall)
                            }]
                        }
                    },

                }) as any,

                inject: [ConfigService],
            },

interceptors are described in "channelOptions" object, inside the array "interceptors" you can pass a function with params (options,next) a typical middleware function, the problem GRPCInterceptor is a NestJS service class

import {InterceptingCall} from "@grpc/grpc-js";
import {Injectable, NestInterceptor, ExecutionContext, CallHandler} from '@nestjs/common';
import {ClsService} from "nestjs-cls";

@Injectable()
export class GRPCInterceptor {

    constructor(
        private readonly cls: ClsService,

    ) {
    }

    interceptGrpcCall(options, nextCall): any {
        const requester:any = {
            start: (metadata, listener, next) => {
                const newListener = {
                    onReceiveMetadata: (metadata, next) => {
                        next(metadata);
                    },
                    onReceiveMessage: (message, next) => {
                        next(message);
                    },
                    onReceiveStatus: (status, next) => {
                        this.cls.set('grpcStatus', status)
                        console.log(this.cls.get("grpcStatus"))
                        console.log('onReceiveStatus');
                        console.log(options?.method_definition?.path);
                        console.log(status);
                        next(status);
                    }
                };
                next(metadata, newListener);
            },
            sendMessage: function (message, next) {
                next(message);
            },
            halfClose: function (next) {
                next();
            },
            cancel: (message, next) => {
                return new InterceptingCall(nextCall(options), requester);

            }
        };
        return new InterceptingCall(nextCall(options), requester);
    }
}

and it is waiting a clsService constructor param

error TS2554: Expected 1 arguments, but got 0.

32                                 new GRPCInterceptor().interceptGrpcCall(options, nextCall)
                                   ~~~~~~~~~~~~~~~~~~~~~

  src/GRPCInterceptor.ts:9:9
    9         private readonly cls: ClsService,
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    An argument for 'cls' was not provided.

I know you cant do "new NestJSClass().myFunction()", so the problem is how i can run the function inside of my nestjsClass in a module context

i use clsModule because i have a logic with some varaibles on runtime so i use it, but i dont know how instantiate a new class and pass it to nestjs class


Solution

  • As GRPCInterceptor is a provider class, you should be able to provide it to the inject array like you do the ConfigService and make it a parameter of the useFactory.

    @Module({
        imports: [
            ConfigModule,
            ClientsModule.registerAsync([
                {
                    name: 'tokenvalidator',
                    imports: [ConfigModule, ClsModule],
    
                    useFactory: async (configService: ConfigService, grpcInterceptor: GRPCInterceptor) => ({
                        transport: Transport.GRPC,
                        options: {
                            package: 'br',
                            protoPath: join(__dirname, '../../protos/tokenvalidator.proto'),
                            url: configService.get('GRPC_TOKEN_VALIDATOR_HOST'),
                            channelOptions: {
                                interceptors: [(options, nextCall) => {
                                    grpcInterceptor.interceptGrpcCall(options, nextCall)
                                }]
                            }
                        },
    
                    }) as any,
    
                    inject: [ConfigService, GRPCInterceptor],
                },