#ExceptionFilter catches factory error, but injected deps are undefined

11 messages · Page 1 of 1 (latest)

inland drum
#

Hi, I have an ExceptionFilter applied to a Controller.
The filter injects request scoped dependency (logger). I know the logger is really provided as I placed an assertion into the filter's constructor.
The controller injects request scoped dependency (dbConn), which is provided via factory.
When the factory throws, the filter catches the exception, but at that moment the logger is suddenly undefined , so an attempt to log the error crashes the app.
None of the deps are durable.
I am using 10.3.3

@Catch()
export class ExtResourceExceptionFilter
  extends BaseExceptionFilter
  implements ExceptionFilter
{
  constructor(
    @Inject(LOGGER) private readonly logger: Logger,
    //httpAdapterHost: HttpAdapterHost,
  ) {
    // Dunno if this is necessary (it's not a global filter),
    // if uncommented, it throws `isHeadersSent` is not a function,
    // which suggests that adapter dependency is unset in the same way as `logger` dependency 
    // super(httpAdapterHost.httpAdapter.getInstance());
    // console.log('constructor', this.logger);
    super();
    assert(this.logger);
  }

  override catch(exception: unknown, host: ArgumentsHost) {
    if (exception instanceof ParseRequestException) {
      super.catch(
        new BadRequestException({
          status: 'fail',
          message: exception.message,
        }),
        host,
      );
    } else if (exception instanceof HttpException) {
      super.catch(exception, host);
    } else {
      // logger is undefined if factory for controller dependency throws
      this.logger.log({
        level: 'ERROR',
        message: inspect(exception),
      });
      super.catch(
        new InternalServerErrorException({
          status: 'error',
          message: 'Internal server error',
        }),
        host,
      );
    }
  }
}
sage atlas
#

If I'm right, an empty object is truthy.

assert(Object.create(null)) // Does not throw
#

I'm not sure about this being enough.

I know the logger is really provided as I placed an assertion into the filter's constructor.

inland drum
#

Dunno why it should be an empty object, but it's not. And even if it would be, it still becomes undefined in catch.

inland drum
#

Found out the constructor actually isn't called. I've seen it being called, but these were probably different instances - not the ones catching the error and failing on missing deps.

junior bison
inland drum
icy cairn
#

REQUEST scoped providers don't work great in enhancers. Instead, you can get the request obejct and then the ContextId from that request object and resolve the correct instance of the provider as outlined in our docs using the ModuleRef

inland drum
#

Thank you for the response.
How do I obtain the REQUEST or moduleRef in the exception filter if it's constructor isn't invoked and dependencies aren't injected?
Could you elaborate a bit on what "don't work great" means? Are there some rules to follow? Can't nestjs enforce these rules? Either by design or with runtime checks?

icy cairn
#

You can get the requerst obejct via host.switchToHttp().getRequest(). If you remove your @Inject(LOGGER) and instead inject the ModuleRef, the injection will work correctly, and you can then get the contextId using the ContextIdFactory as the docs describe

#

As for "don't work great", I mean, sometimes it works, like in guards and pipes, and sometimes it doesn't,. I've usually noticed that being an issue in filters