Manejo de Errores en Clases de Servicios y Controladores en NodeJS

El manejo de errores es una parte crítica en el desarrollo de aplicaciones con NodeJS, especialmente al trabajar con clases de servicio y controladores. A continuación, se presenta un resumen del problema y una solución que mantiene la capacidad de retornar códigos de error adecuados.

Problema

Al extraer la lógica de obtención de datos en una clase de servicio, se pierde la capacidad de generar códigos de error HTTP informativos. Cuando se lanzan errores en la clase de servicio, el controlador los captura y, por defecto, devuelve un código de error 500, lo que puede ser confuso para el cliente que realiza la solicitud.

Solución Propuesta

  1. Definición de Errores Personalizados: Se pueden crear errores personalizados para diferenciar entre los distintos tipos de problemas que puedan ocurrir. Por ejemplo, se puede definir un error para ID inválidos, no encontrados o prohibiciones basadas en la edad.

  2. Uso de Errores Personalizados en la Clase de Servicio: En lugar de lanzar un Error genérico, lanzaremos errores personalizados que incluyan información sobre el código de estado que se debe retornar.

  3. Manejo de Errores en el Controlador: En el controlador, se capturarán estos errores personalizados y se responderá con el código de estado correspondiente.

Implementación

Definición de Errores Personalizados

class InvalidIdError extends Error {
    constructor(message) {
        super(message);
        this.name = "InvalidIdError";
        this.statusCode = 400;
    }
}

class NotFoundError extends Error {
    constructor(message) {
        super(message);
        this.name = "NotFoundError";
        this.statusCode = 404;
    }
}

class MinorsProhibitionError extends Error {
    constructor(message) {
        super(message);
        this.name = "MinorsProhibitionError";
        this.statusCode = 403;
    }
}

Clase de Servicio con Errores Personalizados

import Person from "../models/person.js";
import mongoose from "mongoose";
import { InvalidIdError, NotFoundError, MinorsProhibitionError } from "../errors/customErrors.js";

class PersonService {
    async getPerson(id) {
        if (!mongoose.Types.ObjectId.isValid(id)) {
            throw new InvalidIdError('Invalid ID');
        }

        const person = await Person.findById(id);
        if (!person) {
            throw new NotFoundError('Not found');
        }

        if (person.Age < 18) {
            throw new MinorsProhibitionError('Cannot query for minors');
        }

        return person;
    }
}

export default PersonService;

Controlador con Manejo de Errores

import PersonService from "../services/PersonService.js";

async function getPerson(req, res) {
    const personService = new PersonService();
    try {
        const personData = await personService.getPerson(req.params.id);
        res.status(200).send(personData);
    } catch (error) {
        console.error(error);
        if (error instanceof InvalidIdError || error instanceof NotFoundError || error instanceof MinorsProhibitionError) {
            res.status(error.statusCode).send(error.message);
        } else {
            res.status(500).send("Error retrieving media");
        }
    }
}

export { getPerson };

Conclusiones

Con esta implementación, se logra mantener la separación de responsabilidades entre la clase de servicio y el controlador. La clase de servicio se encarga de la lógica del negocio y puede lanzar errores personalizados que son manejados adecuadamente por el controlador. Esto permite retornar códigos de error HTTP informativos y mejorar la experiencia del cliente al interactuar con la API.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *