Inicio / Artículos

Interceptores de Axios como middleware HTTP: un modelo mental para aplicaciones React

Comprender cómo funcionan los interceptores de solicitud y respuesta de Axios como middleware, qué debe incluirse en cada uno, y cómo centralizan los encabezados de autenticación y el manejo de errores 401 en React.

Editor: REACTAPP.TOP6 min de lectura1357 palabras

Fuentes: Axios Interceptors Explained: The Missing Piece Every Frontend Developer Should Learn.

En una base de código React reciente, las llamadas a la API tienden a crecer poco a poco mediante copias y pegados: cada solicitud crea su propio encabezado Authorization, cada respuesta cuenta con su propio manejo de errores, y cada ruta de fallo repite la misma lógica de recuperación. Eso funciona bien con tres puntos finales, pero se convierte en un problema de mantenimiento al llegar a treinta. Axios ofrece una solución integrada a través de los interceptores, y una vez que se les considere como middleware para el tráfico HTTP, queda claro qué mover a ellos y qué dejar en los componentes.

La duplicación que eliminan los interceptores

Un punto de partida típico es una solicitud para usuarios que incluye un token de acceso directamente:

axios.get("/users", {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Un segundo punto final repite el bloque de encabezados palabra por palabra:

axios.get("/products", {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Y una operación de escritura hace lo mismo, con la carga útil en el medio:

axios.post("/orders", orderData, {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

A primera vista, nada de esto parece dañino. A medida que el proyecto crece, cada solicitud necesita un token, cada respuesta requiere manejo de errores y cada fallo exige la misma reacción. Ese trabajo transversal es precisamente para lo que sirven los interceptores.

Qué es realmente un interceptor

Un interceptor es una función que Axios ejecuta en cada solicitud saliente antes de enviarla, o en cada respuesta entrante antes de que tu código la reciba: un punto de control donde puedes inspeccionar o modificar el tráfico. La respuesta también cuenta con su propio punto de control antes de resolver la promesa que espera tu código React.

El camino general comienza en tu aplicación:

React Application

y continúa a través de ambos puntos de control antes de volver a ella:

↓Request Interceptor↓Axios↓Server↓Response Interceptor↓React Application

Tus componentes nunca se comunican directamente con el servidor; todo fluye a través de la cadena, lo que la convierte en el lugar natural para un comportamiento uniforme.

Middleware, explicado con una recepción

Si has escrito código en Express o Koa, el concepto te resultará familiar: se trata de middleware. De lo contrario, imagina que visitas un edificio de oficinas. No entras directamente a la oficina; el camino comienza contigo:

You

y pasas por dos paradas en el camino:

↓Security Check↓Reception↓Office

La seguridad verifica tu identificación, la recepción confirma tu cita, y solo entonces llegas a la oficina. Ninguna de estas paradas es el destino final; cada una te interrumpe para realizar tareas adicionales, luego te permite continuar o te rechaza.

El middleware es cualquier capa entre dos sistemas que realiza tareas antes de que la solicitud continúe. Sustituye las oficinas por un servidor, partiendo de la aplicación React:

React App

y el interceptor es la capa intermedia:

↓Middleware (Interceptor)↓Server

En otras palabras, los interceptores de Axios son un middleware para las solicitudes y respuestas HTTP en el cliente.

Interceptores de solicitud y respuesta

Axios ofrece dos tipos, registrados con axios.interceptors.request.use(...) y axios.interceptors.response.use(...). Cada llamada acepta un manejador de éxito y, opcionalmente, uno de errores, y devuelve un identificador que se puede pasar posteriormente a eject para eliminarlo.

Interceptores de solicitud

Estos se ejecutan antes de que se envíe una solicitud. El componente realiza la llamada:

Component

y el interceptor lo detecta al momento de enviarla:

↓Request Interceptor↓Server

Tareas típicas de un interceptor de solicitud:

  • adjuntar tokens de autenticación
  • establecer encabezados personalizados como la configuración regional o el ID de la solicitud
  • modificar el contenido de la solicitud
  • registrar el tráfico que se envía

El manejador recibe la configuración de la solicitud y debe devolverla (o una promesa de ella) para que la solicitud pueda continuar; olvidarse de esto es el error típico de los principiantes.

Interceptores de respuesta

Estos se ejecutan después de que el servidor responde, pero antes de que su aplicación reciba el resultado. La respuesta proviene del servidor:

Server

y pasa por el interceptor en su camino hacia el componente:

↓Response Interceptor↓Component

Responsabilidades comunes aquí:

  • gestionar errores en un solo lugar
  • transformar o desempaquetar los datos de la respuesta
  • registrar las respuestas
  • volver a intentar solicitudes fallidas
  • redirigir al usuario, por ejemplo a una página de inicio de sesión

El manejador de éxito se encarga de las respuestas 2xx y el manejador de errores del resto. Si no puede recuperarse, debe devolver una promesa rechazada para que quienes lo llamen sigan viendo el fallo.

Qué sucede detrás de una sola llamada

Una solicitud de una sola línea parece indicar que Axios la envía de inmediato:

axios.get("/users");

Internamente, la misma llamada:

axios.get("/users")

pasa por un proceso más largo antes de que se ejecute su .then():

↓Request Interceptor↓HTTP Request↓Server↓HTTP Response↓Response Interceptor↓.then()↓Your Component

La lógica compartida se ejecuta en cada llamada sin que el lugar donde se realiza la llamada lo sepa.

Por qué es importante centralizarlo a medida que la aplicación crece

Imagínese un proyecto con cien llamadas a API realizadas de forma manual. La solicitud del usuario contiene el encabezado:

axios.get("/users", {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Lo mismo ocurre con la solicitud de productos:

axios.get("/products", {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Y lo mismo sucede con cada operación de escritura, como crear un pedido:

axios.post("/orders", data, {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Si el backend cambia su esquema de autenticación, debe actualizar cada llamada, y cada una que se omita se convierte en un error. Un interceptor almacena esa lógica una sola vez y Axios la aplica en todas partes. Regístrelo en una instancia creada con axios.create(), para que se aplique a su API y no a URLs de terceros.

Caso de uso: adjuntar automáticamente el token de acceso

Este es el motivo más común para adoptar interceptores. Después del inicio de sesión, la aplicación almacena un token de acceso que todos los endpoints protegidos esperan. La llamada comienza como una solicitud normal:

API Call

y el interceptor agrega el encabezado antes de que llegue al servidor:

↓Request Interceptor↓Authorization Header Added↓Server

Ahora cada solicitud lleva el token. Lea este valor dentro del interceptor en cada solicitud en lugar de hacerlo una sola vez al iniciar, de lo contrario nunca se obtendrá un token actualizado.

Caso de uso: manejar respuestas 401 de forma global

Los tokens de acceso caducan. Cuando eso ocurre, el servidor responde con:

401 Unauthorized

Sin interceptores, cada llamada necesita su propio bloque de código para esto. La primera llamada a la API:

API 1

es seguida por el mismo manejo duplicado para cada otro endpoint:

↓401 HandlingAPI 2↓401 HandlingAPI 3↓401 Handling

Un interceptor de respuestas puede, en cambio, capturar todos los 401 Unauthorized en un solo lugar. Cuando lo detecta:

401 Received

puede renovar el token de acceso, volver a enviar la solicitud original y entregar la respuesta exitosa al que la solicitó:

↓Refresh Access Token↓Retry Original Request↓Return Successful Response

El usuario nunca se da cuenta de que el token ha expirado. Dos medidas de seguridad son importantes: marcar como repetida la solicitud en su configuración para que un segundo error 401 no se repita indefinidamente, y hacer que los fallos simultáneos compartan una sola llamada de actualización, como se explica en por qué los errores 401 concurrentes cierran la sesión de los usuarios y cómo solucionarlo con una sola actualización.

Cuándo no vale la pena usar interceptores

Una aplicación pequeña con dos o tres llamadas a la API beneficia poco de la indirección, y el comportamiento oculto puede sorprender al lector que no puede ver de dónde proviene un encabezado. Con docenas o cientos de solicitudes, los interceptores son una de las mejores formas de mantener el manejo de solicitudes limpio y consistente. Úsalos para problemas transversales; la lógica específica de cada punto final debe quedar dentro de ese punto final.

Conclusión

El envío de solicitudes es solo una pequeña parte de las tareas de la capa HTTP. La autenticación, el manejo de errores, el registro, la configuración de las respuestas y la renovación de tokens son funciones transversales, y los interceptores brindan un lugar único para cada una de ellas.

  • Trate los interceptores como middleware del lado del cliente: los interceptores de solicitud determinan qué se envía y los interceptores de respuesta determinan qué llega.
  • Siempre devuelva la configuración desde los interceptores de solicitud y vuelva a lanzar los errores irreparables desde los interceptores de respuesta.
  • Asócialos a una instancia de axios.create() específica para su API.
  • Implemente una lógica de reintentos para códigos 401 para evitar bucles y renovaciones concurrentes.

Lecturas relacionadas