NestJS

Nest

Tutorial Nest: Gestión de errores en controladores

Nest excepciones: manejo y ejemplos. Aprende a manejar excepciones en Nest con ejemplos prácticos y detallados.

La gestión de errores es una parte esencial de cualquier aplicación, ya que permite manejar situaciones inesperadas de manera adecuada, presentando mensajes claros al usuario y evitando que la aplicación falle de manera catastrófica.

NestJS proporciona un enfoque modular y extensible para manejar errores en sus controladores y en otras partes de una aplicación.

Excepciones básicas en NestJS

NestJS viene con un conjunto de excepciones incorporadas que se pueden lanzar desde un controlador. Estas excepciones se envían como respuestas HTTP con un código de estado adecuado.

NotFoundException

La excepción NotFoundException se utiliza cuando un recurso solicitado no se encuentra en la aplicación.

NotFoundException es útil para manejar situaciones en las que se espera un recurso específico, como un registro en una base de datos, pero no está presente.

import { Controller, Get, NotFoundException } from '@nestjs/common';

@Controller('items')
export class ItemsController {
  @Get(':id')
  findOne(id: string): string {
    // Supongamos que no se encuentra el elemento
    if (!item) {
      throw new NotFoundException(`El elemento con el ID ${id} no se encontró`);
    }
    return item;
  }
}

Si el elemento no se encuentra, se lanza una excepción NotFoundException. Esto resultará en una respuesta HTTP con el código de estado 404 Not Found y un mensaje que indica "El elemento con el ID ${id} no se encontró".

UnauthorizedException

La excepción UnauthorizedException se lanza cuando un usuario o solicitud no está autorizado para acceder a un recurso o realizar una acción específica.

Es esencial para el control de acceso y la seguridad.

Ejemplo:

import { UnauthorizedException, Controller, Get } from '@nestjs/common';

@Controller('recursos')
export class RecursosController {
  @Get()
  obtenerRecursos() {
    // Verificar si el usuario tiene autorización
    if (!usuarioTieneAutorizacion()) {
      throw new UnauthorizedException('No está autorizado para acceder a este recurso');
    }
    // Procesar la solicitud si está autorizado
    return 'Recursos obtenidos con éxito';
  }
}

En este ejemplo, si la función usuarioTieneAutorizacion() devuelve false, se lanza una UnauthorizedException, lo que resulta en una respuesta HTTP con el código de estado 401 Unauthorized y el mensaje "No está autorizado para acceder a este recurso".

BadRequestException

La excepción BadRequestException se utiliza cuando una solicitud no es válida.

BadRequestException es útil para manejar situaciones en las que se espera una solicitud con datos específicos, pero los datos no son válidos.

Ejemplo:

import { BadRequestException, Controller, Post, Body } from '@nestjs/common';

@Controller('recursos')
export class RecursosController {
  @Post()
  crearRecurso(@Body() datos: any) {
    // Validar los datos recibidos
    if (!datos || !datos.nombre) {
      throw new BadRequestException('Los datos proporcionados son incorrectos o incompletos');
    }
    // Procesar la creación del recurso si los datos son válidos
    return 'Recurso creado con éxito';
  }
}

En este ejemplo, si los datos recibidos no cumplen con ciertas reglas de validación (en este caso, la presencia de un campo nombre), se lanza una BadRequestException. Esto resultará en una respuesta HTTP con el código de estado 400 Bad Request y el mensaje "Los datos proporcionados son incorrectos o incompletos".

InternalServerErrorException

La excepción InternalServerErrorException se utiliza cuando ocurre un error inesperado en el servidor.

Generalmente, InternalServerErrorException se lanza en situaciones inesperadas o problemas en la lógica interna del servidor.

Ejemplo:

import { InternalServerErrorException, Controller, Get } from '@nestjs/common';

@Controller('recursos')
export class RecursosController {
  @Get()
  obtenerRecursos() {
    try {
      // Lógica para obtener recursos
    } catch (error) {
      throw new InternalServerErrorException('Se produjo un error interno al obtener los recursos');
    }
  }
}

En este ejemplo, si se produce un error interno durante la obtención de los recursos se lanza una InternalServerErrorException. Esto resultará en una respuesta HTTP con el código de estado 500 Internal Server Error y un mensaje que indica "Se produjo un error interno al obtener los recursos".

ForbiddenException

La excepción ForbiddenException se utiliza cuando un usuario o solicitud no está autorizado para acceder a un recurso o realizar una acción específica.

Es útil para el control de acceso y la seguridad.

ForbiddenException es similar a UnauthorizedException, pero se diferencia en que el usuario puede estar autenticado pero no autorizado.

Ejemplo:

import { ForbiddenException, Controller, Put, Param } from '@nestjs/common';

@Controller('recursos')
export class RecursosController {
  @Put(':id')
  actualizarRecurso(@Param('id') id: string) {
    if (!usuarioTienePermisosDeEdicion(id)) {
      throw new ForbiddenException('No tiene permisos para actualizar este recurso');
    }
    // Procesar la actualización si está autorizado
  }
}

En este ejemplo, si la función usuarioTienePermisosDeEdicion(id) devuelve false, se lanza una ForbiddenException. Esto resultará en una respuesta HTTP con el código de estado 403 Forbidden y un mensaje que indica "No tiene permisos para actualizar este recurso".

ConflictException

La excepción ConflictException se utiliza cuando se detecta un conflicto, como intentar crear un recurso que ya existe.

Esta excepción es útil en situaciones donde se requiere una acción especial para resolver el conflicto.

Ejemplo:

import { ConflictException, Controller, Post, Body } from '@nestjs/common';

@Controller('recursos')
export class RecursosController {
  @Post()
  crearRecurso(@Body() datos: any) {
    if (recursoYaExiste(datos.nombre)) {
      throw new ConflictException('El recurso con este nombre ya existe');
    }
    // Procesar la creación del recurso si no existe conflicto
  }
}

En este ejemplo, si la función recursoYaExiste(datos.nombre) devuelve true, se lanza una ConflictException. Esto resultará en una respuesta HTTP con el código de estado 409 Conflict y un mensaje que indica "El recurso con este nombre ya existe".

NotImplementedException

La excepción NotImplementedException se utiliza cuando una funcionalidad no ha sido implementada todavía. Es útil para indicar que una funcionalidad está en desarrollo o que se planea implementar en el futuro.

Ejemplo:

import { NotImplementedException, Controller, Get } from '@nestjs/common';

@Controller('recursos')
export class RecursosController {
  @Get()
  obtenerRecursos() {
    throw new NotImplementedException('Esta función aún no ha sido implementada');
  }
}

En este ejemplo, si se llama a la ruta /recursos para obtener recursos, se lanzará una NotImplementedException con el mensaje "Esta función aún no ha sido implementada".

Sin embargo, esta excepción no generará una respuesta HTTP específica, ya que no se trata de un error relacionado con el protocolo HTTP, sino más bien con la lógica interna de la aplicación.

Creación de excepciones personalizadas

Si las excepciones básicas no satisfacen las necesidades específicas, es posible crear excepciones personalizadas extendiendo la clase base HttpException.

Ejemplo:

import { HttpException, HttpStatus } from '@nestjs/common';

export class CustomException extends HttpException {
  constructor() {
    super('Mensaje personalizado', HttpStatus.BAD_REQUEST);
  }
}

Luego, esta excepción personalizada se puede lanzar de la misma manera que las excepciones incorporadas:

import { Controller, Get } from '@nestjs/common';

@Controller('items')
export class ItemsController {
  @Get()
  findAll() {
    // Supongamos que ocurre algún error
    throw new CustomException();
  }
}

Filtros de excepción

Los filtros de excepción permiten interceptar excepciones lanzadas desde un controlador y transformarlas en respuestas HTTP.

Para crear un filtro de excepción, primero es necesario implementar la interfaz ExceptionFilter.

Ejemplo:

import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Response } from 'express';

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

    response
      .status(status)
      .json({
        statusCode: status,
        timestamp: new Date().toISOString(),
        message: exception.message,
      });
  }
}

Para asociar este filtro a un controlador específico, se utiliza el decorador @UseFilters():

import { Controller, Get, UseFilters } from '@nestjs/common';

@Controller('items')
@UseFilters(new CustomExceptionFilter())
export class ItemsController {
  @Get()
  findAll() {
    // Supongamos que ocurre algún error
    throw new CustomException();
  }
}

Filtros de excepción globales

Si se desea que un filtro de excepción se aplique a todos los controladores y rutas de la aplicación, se puede configurar como un filtro global:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { CustomExceptionFilter } from './custom-exception.filter';

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

Interceptores para transformación de errores

Mientras que los filtros de excepción se centran en el manejo de errores, los interceptores en NestJS pueden ser utilizados para transformar respuestas o errores de una manera más general.

Los interceptores pueden ser útiles, por ejemplo, si se quiere envolver todas las respuestas en un objeto específico o transformar errores antes de que lleguen a un filtro de excepción.

Un interceptor puede interceptar la respuesta de un método de controlador, y tiene la capacidad de transformarla antes de que se envíe al cliente.

Ejemplo de un interceptor que envuelve todas las respuestas en un objeto específico:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(map(data => ({ data })));
  }
}

Para usar este interceptor en un controlador, se puede aplicar el decorador @UseInterceptors():

import { Controller, Get, UseInterceptors } from '@nestjs/common';

@Controller('items')
@UseInterceptors(TransformInterceptor)
export class ItemsController {
  @Get()
  findAll() {
    return ['item1', 'item2'];
  }
}

La respuesta sería:

{
  "data": ["item1", "item2"]
}

Pipes para validación y transformación de errores

Los pipes en NestJS se pueden usar para transformar y validar los datos de entrada. Si la validación falla, un pipe puede lanzar una excepción.

Por ejemplo, utilizando class-validator y class-transformer, es posible definir reglas de validación y luego usar el ValidationPipe para aplicar esas reglas:

import { IsNotEmpty, IsInt } from 'class-validator';

export class CreateItemDto {
  @IsNotEmpty()
  name: string;

  @IsInt()
  price: number;
}

En el controlador, se puede usar ValidationPipe para validar los datos entrantes:

import { Controller, Post, Body } from '@nestjs/common';

@Controller('items')
export class ItemsController {
  @Post()
  create(@Body(new ValidationPipe()) createItemDto: CreateItemDto) {
    // Si la validación falla, se lanzará una excepción
    // De lo contrario, el código de creación del ítem se ejecutará normalmente
  }
}

Si, por ejemplo, se envían datos inválidos para el createItemDto, el ValidationPipe lanzará automáticamente una excepción, que puede ser capturada por un filtro de excepción, como se mencionó anteriormente.

Conclusión

NestJS proporciona múltiples herramientas y capas para gestionar y transformar errores en la aplicación. Desde excepciones incorporadas hasta excepciones personalizadas, filtros de excepción, interceptores y pipes, es posible crear una gestión de errores robusta y personalizada que se adapte a las necesidades específicas del proyecto.

Certifícate en Nest con CertiDevs PLUS

Ejercicios de esta lección Gestión de errores en controladores

Evalúa tus conocimientos de esta lección Gestión de errores en controladores con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Todas las lecciones de Nest

Accede a todas las lecciones de Nest y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Certificados de superación de Nest

Supera todos los ejercicios de programación del curso de Nest y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.

En esta lección

Objetivos de aprendizaje de esta lección

  1. Comprender las excepciones básicas de NestJS.
  2. Diseñar y utilizar excepciones personalizadas.
  3. Implementar filtros de excepción.
  4. Aplicar interceptores y pipes en la gestión de errores.