En los articulos anteriores aprendimos a pedir datos con fetch. Funciona, es nativo y es estándar.
Pero como vimos, tiene sus asperezas: hay que convertir el JSON manualmente, no gestiona los errores HTTP automáticamente y nos obliga a repetir cabeceras (headers) constantemente.
Cuando construimos aplicaciones grandes, a veces necesitamos una capa más cómoda. Una opción muy habitual (aunque no obligatorioa) es Axios.
Axios es una librería basada en promesas que hace lo mismo que fetch, pero con una API más cómoda y funcionalidades avanzadas que nos ahorran bastante código repetido.
Los fetch nativos han mejorado muchisimo los últimos años, y continuan haciéndolo. De forma que Axios ya no es tan imprescindible con era.
Aún así, sigue siendo una opción muy usada, y la encontraréis seguro. Así que vamos a verla.
¿Por qué Axios?
Antes de instalar nada, entendamos las mejoras inmediatas frente a fetch:
- Transformación automática de JSON: Adiós al
response.json(). Axios te devuelve los datos directamente. - Manejo de errores sensato: Si la API devuelve un 404 o un 500, Axios lanza una excepción automáticamente (entra en el
.catch). Confetchteníamos que comprobarresponse.oka mano. - Instancias e Interceptores: El motivo principal por el que mucha gente lo usa.
Instalación
Instalar Axios es muy sencillo. Se instala simplemente como cualquier otro paquete normal, no tiene más misterio.
npm install axios
Creando una Instancia (Singleton)
Un error habitual es importar axios directamente en cada componente (import axios from 'axios').
Si hacéis eso, tendréis que escribir la URL base (https://api.midominio.com) en cada petición. Si mañana la API cambia de dirección, tendréis que editar 50 archivos.
Para evitar esa repetición, crearemos una Instancia Centralizada.
Cread un archivo en src/api/axios.js (o src/lib/axios.js):
import axios from 'axios';
// Creamos una instancia con configuración base
const api = axios.create({
baseURL: 'https://api.tuweb.com/v1',
timeout: 5000, // Si tarda más de 5s, aborta
headers: {
'Content-Type': 'application/json'
}
});
export default api;
Ahora, en vuestros componentes, importaréis este objeto api en lugar de la librería axios.
// En un componente cualquiera
import api from '../api/axios';
// La petición será a: https://api.tuweb.com/v1/usuarios
const response = await api.get('/usuarios');
Interceptores
Los Interceptores son funciones que se ejecutan antes de que la petición salga de nuestra app, o antes de que la respuesta llegue a nuestro componente.
Son como aduanas o peajes por donde pasa todo el tráfico HTTP.
Interceptor de Request
El caso de uso número uno es la Autenticación. Si tenemos un token de usuario (JWT) guardado en el LocalStorage o en un Store de Zustand, queremos enviarlo en todas las peticiones privadas.
En lugar de añadir el header Authorization manualmente en cada llamada, dejamos que el interceptor lo inyecte.
// src/api/axios.js (continuación)
api.interceptors.request.use(
(config) => {
// 1. Buscamos el token (en localStorage o donde lo tengáis)
const token = localStorage.getItem('token');
// 2. Si existe, lo inyectamos en la cabecera
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config; // Importante devolver la config modificada
},
(error) => {
return Promise.reject(error);
}
);
Ahora cualquier llamada api.get('/perfil') llevará el token automáticamente.
Interceptor de Response
El caso de uso principal aquí es el Manejo Global de Errores. ¿Qué pasa si el token ha caducado (Error 401)? No queremos comprobar el error 401 en cada componente.
Queremos que, si ocurre un 401 en cualquier sitio, la app redirija al usuario al Login automáticamente.
api.interceptors.response.use(
(response) => {
// Si la respuesta es correcta, simplemente devolvemos los datos
// Esto nos ahorra poner .data en cada componente
return response.data;
},
(error) => {
// Si la respuesta es un error...
if (error.response) {
// 1. Token caducado o inválido
if (error.response.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login'; // Redirección forzosa
}
// 2. Error de servidor
if (error.response.status === 500) {
console.error("¡El servidor se ha caído!");
}
}
return Promise.reject(error);
}
);
Refactorizando nuestro Componente
Veamos cómo queda nuestro componente ListaUsuarios del artículo anterior tras aplicar esta arquitectura.
import { useState, useEffect } from "react";
import api from "../api/axios"; // Importamos nuestra instancia
export default function ListaUsuarios() {
const [users, setUsers] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
// 1. Sintaxis más limpia
// 2. No hace falta poner la URL completa
// 3. No hace falta .json() (el interceptor ya devolvió response.data)
const data = await api.get("/users");
setUsers(data);
} catch (err) {
// Axios nos da el mensaje de error detallado
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <p>Cargando...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
);
}
Conclusión
Utilizar una instancia de Axios con interceptores es una de esas decisiones que se agradecen cuando el proyecto crece. Centraliza la lógica de red, simplifica los componentes y hace que el mantenimiento sea más llevadero.
Con esto, ya sabemos pedir datos. Pero a veces, hacemos cálculos pesados con esos datos o renderizamos listas gigantescas que ralentizan la app. Es el momento de entrar en la fase final del curso: Optimización y Rendimiento.
Siguiente paso del curso:
