Introducción: Por qué el manejo de errores es la base de un código robusto
Cualquier aplicación que funcione en Node.js, tarde o temprano se enfrenta a situaciones excepcionales: entrada de usuario incorrecta, fallo de red, archivo inexistente en el disco o datos incorrectos de la base de datos. Sin un manejo adecuado de errores, estas situaciones provocan caídas del servidor, fugas de memoria o comportamientos indefinidos. En JavaScript, especialmente en el entorno Node.js, el mecanismo try/catch es una herramienta fundamental para construir sistemas tolerantes a fallos. En este artículo, analizaremos no solo el try/catch clásico, sino también técnicas avanzadas: clases de error personalizadas, trabajo con promesas y funciones asíncronas, así como las mejores prácticas de registro.
Fundamentos: bloques try, catch y finally
La sintaxis básica del manejo de errores en JavaScript es la siguiente:
try { // Código que puede lanzar una excepción const data = JSON.parse('{"name": "Node.js"}'); console.log(data.name);} catch (error) { // Manejo del error console.error('Ocurrió un error:', error.message);} finally { // Código que se ejecutará en cualquier caso console.log('Bloque finally ejecutado');}Cómo funciona:
try— contiene el código potencialmente peligroso.catch— captura la excepción si ocurre. En el parámetroerrorse recibe el objeto de error.finally— se ejecuta siempre, independientemente de si hubo error o no. Ideal para cerrar archivos, liberar recursos o finalizar transacciones.
Es importante recordar: finally se ejecutará incluso después de un return en el bloque try o catch.
Tipos de errores en JavaScript
Los constructores de error incorporados ayudan a clasificar las excepciones con mayor precisión:
Error— clase base para todos los errores.SyntaxError— error de sintaxis (por ejemplo, JSON incorrecto).TypeError— tipo de dato incorrecto (por ejemplo, llamar a algo que no es una función).ReferenceError— referencia a una variable inexistente.RangeError— fuera de los límites permitidos (por ejemplo, recursión).
Ejemplo de manejo de diferentes tipos:
try { const obj = null; console.log(obj.property); // TypeError} catch (err) { if (err instanceof TypeError) { console.log('Error de tipo:', err.message); } else if (err instanceof ReferenceError) { console.log('Variable no encontrada'); } else { console.log('Error desconocido:', err); }}Generación de errores propios: operador throw
A veces es necesario indicar explícitamente que ha ocurrido una situación excepcional en el programa. Para ello se utiliza el operador throw. Puede lanzar cualquier cosa: una cadena, un número, un objeto, pero lo mejor es una instancia de la clase Error o de sus descendientes.
function divide(a, b) { if (b === 0) { throw new Error('División por cero no permitida'); } if (typeof a !== 'number' || typeof b !== 'number') { throw new TypeError('Los argumentos deben ser números'); } return a / b;}
try { const result = divide(10, 0); console.log(result);} catch (err) { console.error(err.name + ': ' + err.message);}¿Por qué es mejor lanzar un Error y no una cadena? El objeto Error contiene la pila de llamadas (stack), lo cual es crítico para la depuración en Node.js. Una cadena no proporciona esa información.
Creación de clases de error personalizadas
En proyectos reales (por ejemplo, un servidor web con Express) es conveniente tener tipos de error propios para diferentes situaciones: ValidationError, NotFoundError, DatabaseError. Esto simplifica el manejo centralizado.
class ValidationError extends Error {