ShaSha
ShaSha

Reputation: 689

Nestjs prisma global exception handler status is always 500

this is the all-exception.filter.ts:

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  constructor(private readonly httpAdapterHost: HttpAdapterHost) {}

  catch(exception: HttpException, host: ArgumentsHost) {
    const { httpAdapter } = this.httpAdapterHost;

    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const message = exception.message;
    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;
    console.log();

    const responseBody = {
      success: false,
      message,
    };

    httpAdapter.reply(ctx.getResponse(), responseBody, status);
  }
}

and this is a service method that returning just one item:

  findOne(id: number) {
    return this.prisma.restaurant.findUniqueOrThrow({
      where: {
        id,
      },
    });
  }

The problem is that findUniqueOrThrow will throw 404 if the item is not found. but in the global filter when I log the status, I always receive a 500 status code.

Upvotes: 1

Views: 3882

Answers (4)

Kim Doyeop
Kim Doyeop

Reputation: 21

I recommend to segregate the HttpExceptionFilter and AllExceptionFilter.

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const status = HttpStatus.INTERNAL_SERVER_ERROR;

    console.log(exception);

    response
      .status(status)
      .json(ResultFactory.getFailureResult(exception.message));
  }
}
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const status = exception.getStatus();

    console.log(exception);

    response
      .status(status)
      .json(ResultFactory.getFailureResult(exception.message));
  }
}

Then, apply the AllExceptionFilter to global filter:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Filters
  app.useGlobalFilters(new AllExceptionsFilter());
  
  ...

  await app.listen(PORT);
}
bootstrap();

And apply the HttpExceptionFilter to Controller class you using. Then that might have the higher priority than AllExceptionFilter. So, httpExceptions caused by Prisma be filtered by HttpExceptionFilter, and only uncaughtException be filtered by AllExceptionFilter.

Below code snippet is example for above:

@ApiTags('인증/인가')
@Controller('api/users')
@UseFilters(HttpExceptionFilter)
export class UsersController {
  constructor(private readonly usersService: UsersService) {}
  
  @Post('/sign-up')
  async signup(@Body() createRequest: CreateUserRequest) {
    return await this.usersService.signup(createRequest);
  }
}

Upvotes: 1

nicklee
nicklee

Reputation: 95

I was also facing this problem before. I think you are using app.useGlobalFilters method. Try using this instead:

app.module.ts

import { APP_FILTER } from "@nestjs/common";

@Module(
 providers: [
   {
     provide: APP_FILTER,
     useClass: AllExceptionsFilter,
   }
 ]
)

This solved my issue.

Upvotes: 0

Joffrey Hernandez
Joffrey Hernandez

Reputation: 1846

Here a full example for filter handling when Prisma.findFirstOrThrow throw not found exception:

notFound.filter.ts

import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';
import { NotFoundError } from '@prisma/client/runtime';
import { Response } from 'express';

@Catch(NotFoundError)
export class NotFoundExceptionFilter implements ExceptionFilter {
  public catch(exception: NotFoundError, host: ArgumentsHost) {

    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    return response.status(404).json({ statusCode: 404, error: 'Not Found' });
  }
}

main.ts

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  ...
  app.useGlobalFilters(new NotFoundExceptionFilter());
  await app.listen(3000);
}

Et voila !

Upvotes: 1

Jay McDoniel
Jay McDoniel

Reputation: 70221

prisma on it's own does not throw an HttpException from Nest, which is where the getStatus() method exists. The error thrown will also fail the exception instanceof HttpException check. You should wrap the call in a try/catch and transform the error to the appropriate exception type so that Nest's filter can handle sending back to proper exception status code

Upvotes: 0

Related Questions