Nestjs, extending BaseExceptionFilter, applicationRef.isHeadersSent is not a function

216 views Asked by At

I'm struggling with my custom exception filter which extends the Nestjs base exception filter. I found that I could inject HttpAdapterHost to provide the applicationRef argument for the BaseExceptionFilter constructor, but this doesn't seem to work: whenever super.catch is called, the app errors with applicationRef.isHeadersSent is not a function .

ApplicationRef is a valid object at this point, but yep, it does not have an isHeadersSent method. What that actually implies, I'm not sure.

How do I approach putting together a custom filter like this? The docs suggest it ought to be possible.

import {
  ArgumentsHost,
  Catch,
  HttpException,
  Injectable,
} from '@nestjs/common';
import { BaseExceptionFilter, HttpAdapterHost } from '@nestjs/core';
import { Response } from 'express';
import { StructuredLogger } from './structured-logger.service';

// Catch any non-http exception and log it nicely with context instead of directly printing to stderr
@Catch()
@Injectable()
export class UncaughtExceptionFilter extends BaseExceptionFilter {
  logger: StructuredLogger;

  constructor(
    readonly baseLogger: StructuredLogger,
    private adapterHost: HttpAdapterHost,
  ) {
    const instance = adapterHost.httpAdapter.getInstance();
    super(instance);
    this.logger = baseLogger.forClass('UncaughtExceptionFilter', 'uncaught');
  }

  catch(exception: any, host: ArgumentsHost) {
    if (exception instanceof HttpException) {
      super.catch(exception, host);
    } else {
      this.logger.error(exception, 'Uncaught exception', {});
      //Respond manually to avoid printing to stderr
      const ctx = host.switchToHttp();
      const response = ctx.getResponse<Response>();
      const status = 500;
      response.status(status).json({
        statusCode: status,
        message: 'Internal server error',
      });
    }
  }
}
1

There are 1 answers

0
user3896248 On

It seems to work when I use adapterHost.httpAdapter directly as the super constructor argument instead of calling .getInstance(). I'm guessing the base class relies on the abstract interface specifically somehow, but I don't really know the details here. More detailed explanations welcomed!