En el desarrollo moderno de aplicaciones web y extensiones de navegador, la capacidad de interceptar y modificar solicitudes de red es una herramienta esencial que ofrece un control avanzado sobre el flujo de datos entre el cliente y el servidor. Esta práctica no solo facilita la depuración y el monitoreo de las peticiones, sino que también permite implementar funcionalidades como la personalización de respuestas, el bloqueo de contenido no deseado o la integración de proxies personalizados. En particular, el ecosistema de extensiones de Chrome requiere métodos eficientes y flexibles para gestionar estas interacciones de red, especialmente en contextos donde las cabeceras y métodos de autenticación se generan de forma dinámica a través de JavaScript. Interceptar las solicitudes en JavaScript se puede abordar principalmente mediante la manipulación de dos APIs clave: fetch y XMLHttpRequest (XHR). Mientras que fetch representa una interfaz moderna y simplificada para realizar peticiones HTTP, XHR es un método más antiguo pero aún ampliamente utilizado que ofrece funcionalidades semejantes aunque con una arquitectura más compleja.
La diferencia fundamental entre ambas es que fetch actúa a través de una función global única, mientras que XHR requiere instanciar objetos y manejar diversos métodos y eventos para controlar la vida de una solicitud. La técnica para interceptar fetch es relativamente directa debido a su simplicidad. Al sobreescribir la función global fetch, es posible insertar una capa intermedia que logre capturar todos los detalles de la solicitud y respuesta. Para esto, se implementa un mecanismo de middlewares que se ejecutan en un modelo de capas apiladas (onion model), permitiendo manipular tanto el objeto Request como la Response de forma asíncrona y ordenada. Esto permite establecer operaciones antes y después de invocar la función fetch original, logrando controlar desde la modificación del URL y los encabezados de la petición hasta la transformación del contenido recibido.
Por ejemplo, al redefinir globalThis.fetch con una función asíncrona que reciba como parámetros las mismas características que la función original, se puede construir un contexto central que encapsule los datos de la solicitud y respuesta, y aplicar allí una serie de middlewares personalizados. Cada middleware tiene la capacidad de ejecutar código antes de delegar el procesamiento al siguiente middleware, y también después de que la petición ha sido cubierta, generando así una cadena de eventos tanto de preprocesamiento como de postprocesamiento. Esta estructura transmite flexibilidad para casos complejos, tales como la introducción de autenticaciones automáticas, el registro detallado de solicitudes o la implementación de proxies selectivos. XMLHttpRequest, al ser una herramienta orientada a objetos con un complejo ciclo de vida basado en eventos, requiere un abordaje distinto.
Debido a que las propiedades como método, URL, cuerpo y manejadores de eventos se configuran en momentos separados, es fundamental retrasar la ejecución del método send hasta haber recopilado toda la información necesaria. En este sentido, la mejor estrategia consiste en heredar o substituir la clase global XMLHttpRequest, sobreescribiendo sus métodos clave como open y send, y almacenando internamente los parámetros de la petición hasta que se invoque send. Durante la llamada a send se puede construir un contexto personalizado que englobe el objeto Request y una Response ficticia, y ejecutar también aquí una cadena de middlewares que permitan inspeccionar y manipular la solicitud antes de despacharla realmente. Para manejar la respuesta y sus eventos, los observadores o listeners registrados originalmente se deben replicar o invocar manualmente, garantizando que el comportamiento esperado del XHR quede intacto para el consumidor de la API. Aunque esta técnica puede resultar más compleja en comparación con fetch, ofrece la posibilidad de interceptar flujos de datos incluso cuando las aplicaciones utilizan métodos heredados o combinados.
La aplicación práctica de estas técnicas se evidencia en proyectos como la extensión de Chrome “Mass Block Twitter”, que requiere bloquear en bloque usuarios spam en Twitter. Dado que Twitter genera dinámicamente cabeceras de autenticación usando JavaScript, analizar y recrear estas cabeceras puede ser extremadamente laborioso. En cambio, interceptar directamente las solicitudes de red existentes permite capturar estas cabeceras ya formateadas y reutilizarlas para invocar de manera eficiente la API interna de bloqueo. A pesar de la existencia de múltiples bibliotecas de terceros que ofrecen funcionalidad similar, como mswjs o xhook, estas a menudo presentan limitaciones notables en contexto de desarrollos para extensiones de Chrome. Mswsj depende forzosamente de service workers, infraestructura que no puede ejecutarse dentro de los Content Scripts, mientras que xhook está menos actualizado y su soporte se limita a solicitudes XHR sin manejo para fetch.
Este escenario impulsó la necesidad de crear soluciones propias altamente personalizadas que consideraran estos requisitos específicos. El diseño de un sistema de interceptación eficaz para fetch y XHR debe tomar en cuenta aspectos clave: la capacidad de modificar la URL de las peticiones para redireccionarlas a proxies, la facultad de invocar el comportamiento nativo original para no modificar radicalmente el flujo esperado, la posibilidad de inspeccionar y mutar las respuestas para adaptar los datos recibidos, y el soporte para flujos de datos continuos como los eventos SSE (Server-Sent Events). Esta complejidad aconseja el desarrollo de APIs sencillas con modelos basados en middlewares, logrando así un patrón reutilizable y escalable, inspirándose en frameworks web populares como hono. Los middlewares actúan aquí como capas apiladas, donde cada una tiene la responsabilidad de hacer algo en la petición o respuesta, y ceder el control a la siguiente. El modelo onion es especialmente útil porque permite ejecutar código tanto antes como después del paso al middleware siguiente, facilitando acciones previas como la inyección de datos o validaciones, y también acciones posteriores como el logging o la modificación de la respuesta final.
Desde el punto de vista de desarrollo, la creación de un interceptor para fetch puede ilustrarse con la redefinición simple de la función fetch global, pero que internamente compone y orquesta múltiples funciones asíncronas que componen la cadena de middlewares. Esta redefinición respeta las interfaces estándar y garantías de uso, retornando una Promise que finalmente resuelve una Response, pero incorporando lógica intermedia que es disparada automáticamente con cada llamada. Para XMLHttpRequest, la sustitución por una clase extendida permite un control granular. Se captura el método y URL en la llamada a open, se almacenan las funciones de callback como listeners para eventos como load o error, y solo cuando send es llamado se ejecuta el procesamiento de los middlewares. Tras ejecutar estos, se efectúa la solicitud real, replicando la sincronización y los eventos esperados.
Aunque este enfoque puede no cubrir exhaustivamente todos los detalles o eventos de XHR en su forma más compleja, suministra una base sólida para interceptar y manipular solicitudes de red en aplicaciones prácticas. La comunidad ha visto el nacimiento de paquetes npm como @rxliuli/vista que encapsulan estas técnicas avanzadas de manera profesional, proporcionando herramientas listas para su integración en proyectos reales, incluyendo extensiones de Chrome y aplicaciones web con necesidades sofisticadas de manipulación de red. Esto reduce significativamente el esfuerzo de implementar con éxito estos mecanismos desde cero y garantiza mantener compatibilidad y rendimiento. En conclusión, interceptar solicitudes de red en JavaScript constituye un recurso valioso para la personalización y mejora del control del tráfico web, especialmente en escenarios donde es necesaria la adaptación dinámica de peticiones y respuestas. El entendimiento profundo de cómo funcionan fetch y XMLHttpRequest, junto a estrategias de diseño basadas en middlewares y modelos asíncronos, permite a desarrolladores aprovechar al máximo el potencial de estos métodos para crear experiencias más ricas, seguras y eficientes.
Gestionar apropiadamente estos procesos contribuye a optimizar extensiones de navegador, depurar aplicaciones y facilitar integraciones complejas, constituyendo así una habilidad fundamental en el desarrollo frontend contemporáneo.