1
votes

I have an api NestJS where I implemented an error filter to catch all kinds of exceptions. In main.ts I set the api to use the filter globally.

Apparently, it only catches the errors in the controllers, because when I throw an exception in the context of service, the exception is thrown in the console and the api falls.

main.ts:

import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import {
  FastifyAdapter,
  NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './exception-filters/http-exception.filter';
import { AllExceptionsFilter } from './exception-filters/exception.filter';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter());

  const options = new DocumentBuilder()
    .setTitle('API Agendamento.Vip')
    // .setDescription('')
    .setVersion('1.0')
    .build();

  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup('api', app, document);
  // app.useGlobalFilters(new HttpExceptionFilter());
  app.useGlobalPipes(new ValidationPipe());
  app.useGlobalFilters(new AllExceptionsFilter());
  await app.listen(process.env.PORT || 3001);
}
bootstrap();

in a service method these exceptions are thrown

  if (err) { throw new InternalServerErrorException('error', err); }

  if (!user) { throw new NotFoundException('device info missing'); }

  if (!user.active) { throw new HttpException('active error', 400); }

my exception filter:

import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus, BadRequestException } from '@nestjs/common';
import { FastifyRequest, FastifyReply } from 'fastify';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response: FastifyReply<any> = ctx.getResponse();
    const request: FastifyRequest = ctx.getRequest();

    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const objResponse = Object.assign(exception, {
      timestamp: new Date().toISOString(),
      path: request.req.url
    });

    response.status(status).send({
      objResponse
    });
  }
}

Exceptions are thrown on the Node console, causing the api to stop.

They are only captured by the filter if they are in the context of the Controller.

What can I do to get exceptions caught in the service as well?

2
can you show your exception filter? - RalphJS
@RalphJS, I edited and includes the exception filter - Christian Santos Grossi
The three exceptions you are using are HttpExceptions, even adding that on the catch decorator doesn't work?, you can also try adding the filter to the global scope using APP_FILTER I'll add an example - RalphJS

2 Answers

1
votes

Having seen your filter we can try two things first adding the HttpException to the @Catch decorator (just for testing I beleive won't make any difference)

@catch(HttpException)
export class AllExceptionFilter implements ExceptionFilter {
  /*your code*/
}

Then instead of using app.useGlobalFilters add in the app module the following:

import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { AllExceptionsFilter } from './exception-filters/exception.filter';

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: AllExceptionFilter,
    },
  ],
})
export class AppModule {}

This will put the filter in the global scope and will give you the capability to use the global injector, this is the aproach I always use for Globals(Filters, Guards, Pipes) and have worked for me like a charm

0
votes

The problem was that I was throwing exceptions from within the model method:

  async signInApp(dataLogin: DataLoginDto) {
        return new Promise((resolve, _reject) => {
            this.userModel.findOne({
                email: dataLogin.email
            }, (err, user) => {

                if (err) { throw new InternalServerErrorException('error', err); }

                if (!user) { throw new NotFoundException('device info missing'); }

// ...Code omitted

I changed the code by placing exceptions outside the scope of mongoose methods:

 async signInApp(dataLogin: DataLoginDto) {
        const user = await this.userModel.findOne({
            email: dataLogin.email
        }).select('password active').exec();

        if (!user) { throw new NotFoundException('user not found'); }

        if (!user.active) { throw new UnauthorizedException('unable to access your account'); }

// ...Code omitted