¿Cuál es el enfoque de manejo de errores de nestjs (error de lógica de negocios versus error de http)?

5 minutos de lectura

Avatar de usuario de Aaron Ullal
Aarón Ullal

Mientras usaba NestJS para crear API, me preguntaba cuál es la mejor manera de manejar errores/excepciones. He encontrado dos enfoques diferentes:

  1. Tener servicios individuales y conductos de validación. throw new Error()tener el controlador catch y luego lanzar el tipo apropiado de HttpException(BadRequestException, ForbiddenException etc..)
  2. Haga que el controlador simplemente llame al método de tubería de servicio/validación responsable de manejar esa parte de la lógica de negocios, y lance el apropiado HttpException.

Hay pros y contras en ambos enfoques:

  1. Esta parece la forma correcta, sin embargo, el servicio puede regresar Error por diferentes razones, ¿cómo puedo saber desde el controlador cuál sería el tipo de HttpException ¿regresar?
  2. Muy flexible, pero teniendo Http las cosas relacionadas en los servicios simplemente parecen estar mal.

Me preguntaba, ¿cuál (si corresponde) es la forma “nest js” de hacerlo?

¿Cómo manejas este asunto?

Avatar de usuario de Kim Kern
kim kern

Supongamos que su lógica de negocios genera un EntityNotFoundError y desea asignarlo a un NotFoundException.

Para eso, puedes crear un Interceptor que transforma tus errores:

@Injectable()
export class NotFoundInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    // next.handle() is an Observable of the controller's result value
    return next.handle()
      .pipe(catchError(error => {
        if (error instanceof EntityNotFoundError) {
          throw new NotFoundException(error.message);
        } else {
          throw error;
        }
      }));
  }
}

Luego puede usarlo agregando @UseInterceptors(NotFoundInterceptor) a la clase o métodos de su controlador; o incluso como un interceptor global para todas las rutas. Por supuesto, también puede mapear múltiples errores en un interceptor.

Pruébalo en este codigosandbox.

  • Tenga en cuenta que el fragmento de código ya usa el nuevo interceptor nest v6. Para ver un ejemplo de v5, eche un vistazo a codesandbox.

    –Kim Kern

    19 de marzo de 2019 a las 11:43


  • El filtro de excepción nestjs parece más adaptado para ese trabajo docs.nestjs.com/exception-filters

    – Alejandro Morgaut

    12 de septiembre de 2022 a las 10:09

Avatar de usuario de Ukpa Uchechi
Ukpa Uchechi

Nest Js proporciona un filtro de excepción que maneja el error no manejado en la capa de la aplicación, por lo que lo modifiqué para que devuelva 500, error interno del servidor para las excepciones que no son Http. Luego, registra la excepción en el servidor, luego puede saber qué está mal y solucionarlo.

import 'dotenv/config';
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus, Logger } from '@nestjs/common';

@Catch()
export class HttpErrorFilter implements ExceptionFilter {
  private readonly logger : Logger 
  constructor(){
    this.logger = new Logger 
  }
  catch(exception: Error, host: ArgumentsHost): any {
    const ctx = host.switchToHttp();
    const request = ctx.getRequest();
    const response = ctx.getResponse();

    const statusCode = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR
    const message = exception instanceof HttpException ?  exception.message || exception.message?.error: 'Internal server error'

    const devErrorResponse: any = {
      statusCode,
      timestamp: new Date().toISOString(),
      path: request.url,
      method: request.method,
      errorName: exception?.name,
      message: exception?.message
    };

    const prodErrorResponse: any = {
      statusCode,
      message
    };
    this.logger.log( `request method: ${request.method} request url${request.url}`, JSON.stringify(devErrorResponse));
    response.status(statusCode).json( process.env.NODE_ENV === 'development'? devErrorResponse: prodErrorResponse);
  }
}

  • ¡Buena implementación! Creo que también puede usar la sintaxis de inyección de dependencia de NestJS, por lo que no tiene que declarar un “registrador” de propiedad privada y luego crear una instancia. Simplemente puede usar “registrador privado de solo lectura: registrador” dentro del constructor y se crea una instancia automáticamente.

    – Marcus Castanho

    6 de enero de 2022 a las 14:25

  • Gracias, anotado implementará y actualizará la respuesta.

    – Ukpa Uchechi

    6 de enero de 2022 a las 14:34


  • Al menos para mi caso de uso, esta es la mejor respuesta.

    – noamtm

    15 de mayo de 2022 a las 13:53

  • FYI @MarcusCastanho: parece que no puede usar NestJS DI, al menos cuando usa un filtro global, porque tiene que crear una instancia del filtro usted mismo.

    – noamtm

    15 de mayo de 2022 a las 13:54

  • Para devolver el mensaje de error correcto, debe hacer algo como esto: const message = exception instanceof HttpException ? exception.getResponse()?.['message'] ? exception.getResponse()?.['message'] : exception['message'] : 'Internal server error';

    – Lucas Vellido

    9 de junio de 2022 a las 15:08


Es posible que desee vincular servicios no solo a la interfaz HTTP, sino también a GraphQL o cualquier otra interfaz. Por lo tanto, es mejor convertir las excepciones de nivel de lógica empresarial de los servicios a las excepciones de nivel Http (BadRequestException, ForbiddenException) en los controladores.

De la manera más simple que podría parecer

import { BadRequestException, Injectable } from '@nestjs/common';

@Injectable()
export class HttpHelperService {
  async transformExceptions(action: Promise<any>): Promise<any> {
    try {
      return await action;
    } catch (error) {
      if (error.name === 'QueryFailedError') {
        if (/^duplicate key value violates unique constraint/.test(error.message)) {
          throw new BadRequestException(error.detail);
        } else if (/violates foreign key constraint/.test(error.message)) {
          throw new BadRequestException(error.detail);
        } else {
          throw error;
        }
      } else {
        throw error;
      }
    }
  }
}

y entonces

  • Gracias alex ¿Cómo usarías el código que publicaste? en el controlador?

    – Aarón Ullal

    13 de agosto de 2018 a las 16:07

  • ¿Cómo implementaron este servicio?

    – Lewsmith

    9 de febrero de 2020 a las 16:47

Avatar de usuario de Dario Palminio
Darío Palminio

También puede usar una fábrica o un controlador para que, cuando el controlador detecte la excepción (error o error de dominio), la asigne a otra HttpException.

@Controller('example')
export class ExampleController {

  @Post('make')
  async make(@Res() res, @Body() data: dataDTO): Promise<any> {
   
    try {
      //process result...
       return res.status(HttpStatus.OK).json(result);
    } catch (error) {
      throw AppErrorHandler.createHttpException(error); //<---here is the error type mapping
    };
  };

};

¿Ha sido útil esta solución?