Capítulo 4.4: Gestión de errores
Tiempo de estudio: 45 minutos
1. Gestión de errores: Protocolos de emergencia del Centro de Control de Misiones (CCM)
En el espacio, no todo puede salir según lo planeado: una llamarada solar puede interrumpir las comunicaciones, el ordenador de a bordo de la nave puede fallar y un comando desde la Tierra puede contener coordenadas incorrectas.
La gestión de errores en el frontend son los protocolos de emergencia de su CCM. Deben:
- 🚨 Evitar que toda la interfaz "explote" debido a un solo comando fallido.
- 📡 Comunicar claramente al operador (usuario) qué salió mal.
- 🔧 Sugerir posibles acciones futuras.
💡 Analogía espacial:
Si la nave envía una señal
500 Internal Server Error
, en la pantalla del CCM no debería aparecer "Error crítico de JavaScript en la línea 57". En su lugar, debería ser: "🚨 ¡Fallo a bordo de la nave! Los ingenieros ya han sido notificados. Intente repetir el comando más tarde."
2. Tipos de "anomalías cósmicas"
En el frontend, nos encontramos con tres tipos principales de errores al trabajar con la API:
- Errores de red: La conexión con el servidor no se ha establecido. La antena no funciona, el cable está cortado.
fetch
"caerá" en el bloque.catch()
. - Errores del cliente (4xx): El comando desde la Tierra era incorrecto. ID inválido, error de validación. El servidor responde, pero con un estado
4xx
. - Errores del servidor (5xx): Fallo en la propia nave. Problema en el código de la API. El servidor responde, pero con un estado
500+
.
Ya hemos empezado a gestionarlos con try...catch
y la comprobación de response.ok
. Ahora, hagámoslo de forma centralizada.
3. Función manejadora centralizada
Repetir el mismo código try...catch
en cada función es una mala práctica. Crearemos un "envoltorio" universal para nuestras peticiones fetch
.
Paso 1: Creamos api.js
Cree un nuevo archivo api.js
junto a app.js
. En él, trasladaremos toda la lógica de interacción con la API.
// api.js
const API_BASE_URL = 'http://127.0.0.1:8000';
/**
* Función universal para realizar peticiones a la API.
* Gestiona errores y devuelve JSON.
* @param {string} endpoint - Punto final de la API, por ejemplo, '/spaceships'
* @param {object} options - Parámetros para fetch (method, headers, body)
*/
async function apiRequest(endpoint, options = {}) {
const url = `${API_BASE_URL}${endpoint}`;
try {
const response = await fetch(url, options);
// Si la respuesta no es JSON en absoluto, lanzamos un error inmediatamente
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
// Excepción para una petición DELETE exitosa que no tiene cuerpo
if (response.status === 204) return null;
throw new TypeError(`Respuesta no-JSON recibida del servidor: ${response.statusText}`);
}
const data = await response.json();
if (!response.ok) {
// Si el servidor devolvió un JSON con un error (por ejemplo, detalle de FastAPI)
const errorMessage = data.detail || `¡Error HTTP! Estado: ${response.status}`;
throw new Error(errorMessage);
}
return data;
} catch (error) {
console.error(`Error en la petición API a ${endpoint}:`, error);
// "Relanzamos" el error para que pueda ser capturado en la UI
throw error;
}
}
Paso 2: Conectamos api.js
en index.html
Es importante conectarlo ANTES de app.js
, ya que app.js
utilizará sus funciones.
<!-- index.html -->
<body>
<!-- ... -->
<script src="api.js"></script>
<script src="app.js"></script>
</body>
Paso 3: Refactorizamos app.js
Ahora reescribiremos nuestras funciones usando el nuevo apiRequest
.
// app.js
// const API_BASE_URL = ...; // Esta línea puede eliminarse, ahora está en api.js
// ...
async function fetchAndDisplayFleet() {
try {
fleetList.innerHTML = '<li>Cargando telemetría...</li>';
const ships = await apiRequest('/spaceships'); // <-- ¡Usamos nuestro envoltorio!
fleetList.innerHTML = '';
if (ships.length === 0) {
fleetList.innerHTML = '<li>No hay ninguna nave en el registro.</li>';
return;
}
ships.forEach(ship => { /* ... el resto del código de visualización ... */ });
} catch (error) {
fleetList.innerHTML = `<li>🔴 Error al cargar la flota: ${error.message}</li>`;
}
}
async function createShip(event) {
event.preventDefault();
const shipData = { /* ... recolección de datos del formulario ... */ };
try {
createStatusMessage.textContent = 'Enviando comando de lanzamiento...';
const options = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(shipData)
};
const newShip = await apiRequest('/spaceships', options); // <-- ¡Usamos nuestro envoltorio!
createStatusMessage.textContent = `🚀 ¡Lanzamiento exitoso! ID asignado a la nave: ${newShip.id}`;
createShipForm.reset();
fetchAndDisplayFleet();
} catch (error) {
createStatusMessage.textContent = `🔴 Error: ${error.message}`;
}
}
// ¡Reescriba el resto de las funciones (fetchShipById, deleteShip) de manera análoga!
response.ok
y parsear JSON se encuentra en un solo lugar, y el código en app.js
se ha vuelto mucho más limpio y legible.
4. Mostrar errores al usuario
Una buena interfaz no solo debe escribir el error en la consola, sino mostrarlo al usuario de forma comprensible.
Ejemplo: Mejora de createShip
Nuestro código ya lo hace: createStatusMessage.textContent = ...
. Pero podemos mejorarlo aún más creando una función universal para mostrar notificaciones.
Añadimos a app.js
:
// app.js
function showNotification(message, isError = false) {
const notificationArea = document.getElementById('create-status-message'); // o otro elemento
notificationArea.textContent = message;
notificationArea.style.color = isError ? 'red' : 'green';
}
// Usar en createShip:
async function createShip(event) {
// ...
try {
// ...
const newShip = await apiRequest('/spaceships', options);
showNotification(`🚀 ¡Lanzamiento exitoso! ID: ${newShip.id}`);
// ...
} catch (error) {
showNotification(`🔴 Error: ${error.message}`, true);
}
}
Cuestionario de repaso
🚀 Resumen del capítulo:
Usted ha fortalecido su MCC (Centro de Control de Misión) creando protocolos de emergencia fiables.
- 🛡️ Usted comprende la diferencia entre errores de red, de cliente y de servidor.
- ⚙️ Usted ha creado una función
apiRequest
centralizada para manejar todas las solicitudes, evitando la duplicación de código. - 📡 Su interfaz ahora es capaz de informar correctamente al usuario sobre los errores, haciéndola más amigable y confiable.
¡Escudos de emergencia levantados! Pero, ¿qué es mejor: las cadenas .then()
o el moderno async/await
? En el próximo capítulo, analizaremos ambos enfoques y entenderemos cuándo usar cada uno.
📌 Verificación:
- Verifique que su código en
app.js
haya sido refactorizado con éxito y utilice la nueva funciónapiRequest
.- Intente detener el servidor FastAPI y haga clic en el botón "Solicitar datos". Debería ver un error de conexión en la página.
- Intente crear una nave con datos inválidos. Debería ver un mensaje de error de validación que proviene de FastAPI.
⚠️ Si hay errores:
apiRequest is not defined
: Asegúrese de haber conectadoapi.js
enindex.html
antes deapp.js
.- Verifique la consola del navegador en busca de otros errores de sintaxis en JavaScript.