#NestJs global exception filter does not resolve values from "nest-cls" package's service

7 messages · Page 1 of 1 (latest)

cosmic reef
#

I use "nest-cls" to integrate my NestJS application with async local storage

I configured nestjs-cls with;

  export const setupAsyncStorageFactory = (
      configService: ConfigService<ConfigurationInterface, true>,
  ): ClsModuleOptions => ({
      global: true,
      interceptor: {
          mount: true,
          generateId: true,
          idGenerator: (context: ExecutionContext) => {
              const request = context.switchToHttp().getRequest<Request>();
              const nodeEnv = configService.get("nodeEnv", { infer: true });
              const isCloudEnv = nodeEnv === "production" || nodeEnv === "staging";
  
              return isCloudEnv ? request.header("x-request-id") ?? "no-id" : randomUUID();
          },
      },
  });

Then I use this function in my "app.module.ts" file:

            ClsModule.forRootAsync({
                global: true,
                imports: [ConfigModule],
                inject: [ConfigService],
                useFactory: setupAsyncStorageFactory,
            }),

I try to resolve the ClsService within a global exception filter I configured in my "app.module.ts":

        providers: [
            {
                provide: APP_FILTER,
                useClass: AllExceptionsFilter,
                scope: Scope.REQUEST,
            },

And the filter itself:

    @Catch()
    export class AllExceptionsFilter implements ExceptionFilter {
        constructor(
            private readonly httpAdapterHost: HttpAdapterHost,
            private readonly clsService: ClsService<AsyncStorageStore>
        ) {}
    
        public catch(exception: unknown, host: ArgumentsHost) {
            console.log(this.clsService.getId());
        }
    }

But I get undefined instead. I have a global interceptor declared in the same manner (with APP_FILTER in "app.module.ts" file) where I do succeed to get the request ID. So why not in exception filter?

waxen wedge
#

I think that you don't need request scope on AllExceptionsFilter, try deleting scope: Scope.REQUEST.
I think when injecting a request scoped provider you also need to injected other request scoped providers in it.

cosmic reef
#

@waxen wedge
I deleted and it didn't help

waxen wedge
#

Sorry to hear that.
Try mount CLS in middleware instead of interceptor:

ClsModule.forRoot({
      global: true,
      middleware: {
        mount: true,
        generateId: true,
        idGenerator: (context: ExecutionContext) => {
          const request = context.switchToHttp().getRequest<Request>();
          const nodeEnv = configService.get('nodeEnv', { infer: true });
          const isCloudEnv = nodeEnv === 'production' || nodeEnv === 'staging';

          return isCloudEnv ? (request.header('x-request-id') ?? 'no-id') : randomUUID();
        },
      },
    }),

I think that middlewares have the broader scope than exception filter, while interceptors has the shorter scope than interceptors. Maybe that can couse the problem. Also check this request lifecycle: https://ru-nestjs-docs.netlify.app/faq/request-lifecycle#summary

cosmic reef
#

Switching to middleware indeed resolved the issue
@waxen wedge

waxen wedge
#

Nice!
Here is also the diagram of request lifecycle, its good to know when each provider is activated:

plain olive