Lección 4 de 411 min
Datos remotos con Axios
Peticiones, instancias, interceptores, manejo de errores y cancelación, comparados con fetch y HttpClient.
Casi todas las apps hablan con un servidor: piden la lista de pedidos, envían un formulario, suben una foto. El navegador trae fetch para eso, pero en cuanto la app crece aparecen las mismas necesidades: una URL base, el token de sesión en cada petición, un tiempo máximo de espera y un tratamiento de errores coherente.
Axios es una librería que resuelve justo eso y que encontrarás en muchísimos proyectos de React, Vue y Node.js. En esta lección verás cómo se usa y cómo se compara con fetch y con HttpClient, la herramienta que Angular trae de serie y que, en un proyecto Angular, suele ser la mejor opción.
Axios en pocas palabras
Qué es Axios
Un cliente HTTP basado en promesas que funciona igual en el navegador y en Node.js.
- Basado en:
- promesas
- Funciona en:
- navegador y Node.js
Axios es una librería para hacer peticiones HTTP. Cada llamada devuelve una promesa que se resuelve con un objeto respuesta: el cuerpo, ya convertido, está en respuesta.data, y además tienes status y headers.
Funciona en el navegador y en Node.js con la misma API, convierte automáticamente a JSON los objetos que envías y los que recibes, y añade lo que fetch no trae de serie: instancias configuradas, interceptores, tiempo máximo de espera y errores para las respuestas 4xx y 5xx.
Úsalo cuando
En proyectos de React, Vue o Node.js que necesitan una configuración común para todas las peticiones, o cuando el mismo código debe funcionar en el navegador y en el servidor.
Ten en cuenta
Es una dependencia más que mantener y actualizar, para algo que fetch ya hace en parte. En Angular, HttpClient cubre lo mismo y se integra con el resto del framework.
import axios from "axios";
// GET con parámetros: /api/pedidos?pagina=2
const { data: pedidos } = await axios.get<Pedido[]>("/api/pedidos", {
params: { pagina: 2 },
});
// POST: el objeto se envía como JSON automáticamente
const respuesta = await axios.post("/api/pedidos", { producto: 7, cantidad: 2 });
console.log(respuesta.status); // 201Usar Axios bien
Instancias: axios.create, baseURL y timeout
Una configuración común para todas las peticiones a la misma API.
axios.create() crea una instancia con su propia configuración: baseURL se antepone a cada ruta, timeout fija cuántos milisegundos esperar antes de rendirse y headers añade cabeceras comunes. Después escribes api.get("/pedidos") en lugar de repetir la URL completa.
Es habitual tener una instancia por API (la tuya, la de pagos, la de mapas), cada una en su módulo, para que el resto del código no tenga que saber a qué servidor habla.
Úsalo cuando
Siempre que hagas más de un par de peticiones a la misma API: es el sitio natural para la URL base, el tiempo de espera y los interceptores.
Ten en cuenta
Por defecto, Axios no tiene tiempo máximo de espera (timeout: 0): si el servidor no responde, la petición puede quedarse colgada indefinidamente. Fija siempre uno razonable.
import axios from "axios";
export const api = axios.create({
baseURL: "https://api.ejemplo.com/v1",
timeout: 10_000, // a los 10 segundos, se rinde
headers: { "Accept-Language": "es" },
});
// En cualquier otro archivo
const { data } = await api.get<Producto[]>("/productos"); // …/v1/productosInterceptores: el token y su renovación
Funciones que se ejecutan antes de cada petición o después de cada respuesta.
Un interceptor de petición recibe la configuración antes de que la petición salga y puede modificarla: el caso típico es añadir la cabecera Authorization con el token de sesión. Un interceptor de respuesta ve cada respuesta, o cada error, antes que tu código.
Con este último se implementa la renovación del token: si una petición falla con 401 porque el token ha caducado, el interceptor pide uno nuevo, repite la petición original y tu código ni se entera. En el módulo de autenticación verás qué son esos tokens (JWT, OAuth) y por qué caducan.
Úsalo cuando
Para todo lo transversal: autenticación, cabeceras comunes, registro de peticiones, mensajes de error globales o la renovación de tokens.
Ten en cuenta
Marca la petición reintentada para no entrar en un bucle si la renovación también falla con 401. Y si varias peticiones caducan a la vez, comparte una sola renovación en lugar de lanzar una por cada una.
api.interceptors.request.use((config) => {
const token = sesion.token();
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
api.interceptors.response.use(
(respuesta) => respuesta,
async (error) => {
const original = error.config;
if (error.response?.status === 401 && !original._reintento) {
original._reintento = true; // evita el bucle infinito
await sesion.renovar(); // pide un token nuevo
return api(original); // repite la petición original
}
return Promise.reject(error);
},
);Manejo de errores
A diferencia de fetch, Axios rechaza la promesa si el servidor responde 4xx o 5xx.
Axios considera error cualquier respuesta fuera del rango 2xx: un 404 o un 500 rechazan la promesa y caen en tu catch. Con fetch no pasa: solo rechaza si falla la red, y tienes que comprobar tú respuesta.ok.
El error distingue los casos: si existe error.response, el servidor respondió, y ahí tienes status y data; si solo existe error.request, la petición salió pero no hubo respuesta (sin conexión, tiempo agotado). axios.isAxiosError(error) te dice si el error viene de Axios y, en TypeScript, le da el tipo correcto.
Úsalo cuando
En cada llamada que pueda fallar, que son todas: distingue los errores del servidor, que puedes explicar al usuario, de los fallos de red, que quizá merezca la pena reintentar.
Ten en cuenta
Si necesitas tratar ciertos códigos como respuestas normales (un 404 que significa «aún no existe»), la opción validateStatus cambia qué estados cuentan como éxito. Y no muestres error.message al usuario: está pensado para desarrolladores.
import axios from "axios";
try {
const { data } = await api.get<Pedido>(`/pedidos/${id}`);
mostrar(data);
} catch (error) {
if (!axios.isAxiosError(error)) throw error; // no viene de Axios: que suba
if (error.response) {
// El servidor respondió con 4xx o 5xx
if (error.response.status === 404) avisar("Ese pedido no existe.");
else avisar("El servidor ha tenido un problema. Inténtalo más tarde.");
} else {
// No hubo respuesta: sin conexión o tiempo agotado
avisar("No hay conexión con el servidor.");
}
}Cancelación con AbortController
Pasa el signal de un AbortController para cancelar una petición que ya no necesitas.
Axios admite la API estándar del navegador para cancelar: creas un AbortController, pasas su signal en la configuración de la petición y llamas a abort() cuando la respuesta deja de interesarte. La promesa se rechaza con un error de cancelación, que reconoces con axios.isCancel(error).
Es lo que necesitas en un buscador, para cancelar la búsqueda anterior al teclear de nuevo, o al salir de una pantalla con peticiones en marcha. La antigua API CancelToken está obsoleta.
Úsalo cuando
Cuando una respuesta puede dejar de ser útil antes de llegar: búsquedas, filtros, pestañas que se cambian rápido o componentes que se destruyen.
Ten en cuenta
Cancelar en el cliente no deshace nada en el servidor: si la petición ya había llegado, el pedido puede haberse creado igualmente. Y trata la cancelación aparte de los errores reales, para no mostrar «algo ha fallado» cuando fuiste tú quien canceló.
let controlador: AbortController | undefined;
async function buscar(texto: string) {
controlador?.abort(); // cancela la búsqueda anterior
controlador = new AbortController();
try {
const { data } = await api.get("/buscar", {
params: { q: texto },
signal: controlador.signal,
});
pintar(data);
} catch (error) {
if (axios.isCancel(error)) return; // cancelada a propósito: no es un fallo
avisar("No se pudo buscar.");
}
}Axios, fetch o HttpClient
fetch, la API nativa
La función del navegador para hacer peticiones: sin dependencias y también en Node.js.
fetch viene con el navegador y, en las versiones actuales, también con Node.js. Devuelve una promesa con un objeto Response, y el cuerpo se lee aparte con respuesta.json() o respuesta.text().
Es más básica que Axios: no rechaza la promesa con un 404 o un 500 (tienes que mirar respuesta.ok), no tiene interceptores y, para enviar JSON, conviertes tú el cuerpo con JSON.stringify y pones la cabecera. El tiempo máximo de espera se consigue con AbortSignal.timeout().
Úsalo cuando
Para peticiones sencillas, scripts y proyectos que quieren cero dependencias. Muchas apps escriben una pequeña función propia sobre fetch y no necesitan nada más.
Ten en cuenta
El error más común es olvidar comprobar respuesta.ok: el código sigue como si todo hubiera ido bien e intenta leer como datos la página de error del servidor.
const respuesta = await fetch("/api/pedidos", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ producto: 7, cantidad: 2 }),
signal: AbortSignal.timeout(10_000), // tiempo máximo: 10 segundos
});
if (!respuesta.ok) {
// fetch no rechaza con 4xx ni 5xx: hay que comprobarlo a mano
throw new Error(`Error ${respuesta.status}`);
}
const pedido = await respuesta.json();HttpClient de Angular
El cliente HTTP de Angular: observables, interceptores funcionales y pruebas integradas.
- Paquete:
- @angular/common/http
- Devuelve:
- observables
HttpClient es el cliente HTTP que trae Angular. Se activa con provideHttpClient() en la configuración de la app y se inyecta en tus servicios. Sus métodos devuelven observables tipados: http.get<Pedido[]>(url) emite el cuerpo ya convertido y termina.
Cubre lo que aporta Axios, pero integrado en el framework: interceptores funcionales registrados con withInterceptors, errores HttpErrorResponse para los 4xx y 5xx, cancelación al desuscribirte (que encaja con switchMap), utilidades de prueba con provideHttpClientTesting y, si renderizas en el servidor, reutilización de las respuestas al hidratar la página.
Con withFetch() usa fetch por debajo en lugar del antiguo XMLHttpRequest, algo recomendable si renderizas en el servidor.
Úsalo cuando
En proyectos Angular, casi siempre: encaja con la inyección de dependencias, con RxJS, con los signals (toSignal, httpResource) y con las herramientas de prueba del framework.
Ten en cuenta
Sus observables son perezosos: si nadie se suscribe (con subscribe, async o toSignal), la petición no sale. Y cada suscripción lanza una petición nueva.
export const authInterceptor: HttpInterceptorFn = (peticion, siguiente) => {
const token = inject(SesionService).token();
return siguiente(peticion.clone({ setHeaders: { Authorization: `Bearer ${token}` } }));
};
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [provideHttpClient(withFetch(), withInterceptors([authInterceptor]))],
};
// pedidos.service.ts
@Injectable({ providedIn: "root" })
export class PedidosService {
private readonly http = inject(HttpClient);
listar() {
return this.http.get<Pedido[]>("/api/pedidos"); // Observable<Pedido[]>
}
}¿Y Axios en Angular?
Funciona, pero te quedas fuera de los interceptores de Angular, de HttpTestingController y de la reutilización de respuestas al hidratar, y mezclas promesas con observables. Salvo que compartas código con un proyecto que ya use Axios, HttpClient es la opción natural.
Interactivo
Axios, fetch y HttpClient
Activa o desactiva opciones para compararlas cara a cara.
| Característica | Axios | fetch | HttpClient |
|---|---|---|---|
| Qué es | Librería externa | API nativa del navegador y de Node.js | Parte de Angular |
| Devuelve | Promesas, con data ya convertido | Promesas; el cuerpo se lee aparte | Observables tipados |
| Errores 4xx y 5xx | Rechaza la promesa | No rechaza: mira respuesta.ok | Emite un HttpErrorResponse |
| Interceptores | Sí, de petición y de respuesta | No | Sí, funcionales, con withInterceptors |
| Cancelación | AbortController | AbortController | Desuscribirse, por ejemplo con switchMap |
| Dónde encaja | React, Vue y Node.js | Peticiones sencillas, cero dependencias | Proyectos Angular |
Ponlo en práctica
Saber qué es cada cosa es la mitad del trabajo. La otra mitad es reconocer cuándo usarla.
Interactivo
¿Qué usarías?
Lee cada situación y elige la opción que mejor la resuelve. Fallar aquí no cuenta: es para practicar.
Empiezas una app Angular que consume una API REST con autenticación por token, y quieres probar los servicios sin un servidor real.
Para recordar
- Axios es un cliente HTTP basado en promesas para el navegador y Node.js, con instancias, interceptores,
timeouty errores para 4xx y 5xx. - Crea una instancia por API con
baseURLytimeout: por defecto, Axios no tiene tiempo máximo de espera. - Los interceptores son el sitio del token y de su renovación; evita bucles marcando la petición ya reintentada.
fetchno rechaza la promesa con 4xx ni 5xx: comprueba siemprerespuesta.ok.- En Angular,
HttpClientcubre lo mismo que Axios y se integra con RxJS, los signals y las pruebas: es la opción por defecto.
Comprueba lo aprendido
Cuestionario
Responde las 5 preguntas para completar la lección.
1.El servidor responde 404. ¿Qué hacen Axios y
fetch?2.¿Para qué sirve
axios.create()?3.En un error de Axios existe
error.request, pero noerror.response. ¿Qué significa?4.Un interceptor de respuesta renueva el token ante un 401 y repite la petición. ¿Qué riesgo hay que evitar?
5.En un proyecto Angular, ¿por qué se suele preferir
HttpClienta Axios?