Cliente HTTP en JavaScript: técnicas avanzadas de solicitudes en Node.js

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Cliente HTTP en JavaScript: haciendo solicitudes correctamente — nivel avanzado



Las solicitudes HTTP básicas con fetch o axios son solo la punta del iceberg. En aplicaciones de producción reales en Node.js te encuentras con decenas de matices: pools de conexiones, reintentos ante fallos, interceptores para registro, streaming de grandes datos y ajuste fino de tiempos de espera. En este artículo analizaremos técnicas avanzadas para trabajar con clientes HTTP en JavaScript. Aprenderás a gestionar keep-alive, implementar un mecanismo de reintento confiable, usar interceptores y crear agentes personalizados. El material está dirigido a desarrolladores que ya conocen los fundamentos de HTTP y quieren escribir código más eficiente y tolerante a fallos.



1. Gestión de conexiones: Keep-Alive y agentes personalizados



Por defecto, Node.js utiliza un pool global de agentes HTTP. Sin embargo, para sistemas de alta carga es importante controlar la cantidad de conexiones simultáneas y su reutilización. Veamos la configuración de keep-alive usando el módulo http y la biblioteca undici.



Ejemplo con agente personalizado (Node.js built-in):

const http = require('http');const https = require('https');

// Creamos un agente personalizado con pool de conexionesconst agent = new http.Agent({ keepAlive: true, maxSockets: 50, // Máximo 50 sockets simultáneos maxFreeSockets: 10, // No más de 10 libres en el pool timeout: 60000, // Tiempo de espera de inactividad del socket (ms)});

const options = { hostname: 'api.example.com', port: 80, path: '/data', agent: agent,};

const req = http.get(options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => console.log('Respuesta recibida:', data));});

req.on('error', (e) => console.error('Error:', e.message));


Enfoque moderno con undici (más rápido y ligero):

const { Client, Pool } = require('undici');

// Creamos un pool con keep-aliveconst client = new Pool('https://api.example.com', { connections: 100, pipelining: 10, keepAliveTimeout: 30000,});

// Realizamos la solicitudclient.request({ method: 'GET', path: '/data',}).then(({ statusCode, body }) => { console.log('Estado:', statusCode); body.setEncoding('utf8'); body.on('data', console.log);}).catch(err => console.error(err));


El uso de agentes personalizados permite evitar problemas de fugas de sockets y mejora el rendimiento mediante la reutilización de conexiones.



2. Mecanismo confiable de reintentos (Retry)



Las redes son inestables. Incluso fallos temporales pueden provocar pérdida de datos. Una estrategia de reintento adecuada debe considerar el tipo de error, el número de intentos y una demora exponencial. Implementemos un decorador de reintento universal para cualquier cliente HTTP.



const axios = require('axios');const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

async function withRetry(requestFn, retries = 3, baseDelay = 1000) { for (let attempt = 1; attempt <= retries; attempt++) { try { return await requestFn(); } catch (error) { // No reintentamos en errores de cliente (4xx) if (error.response && error.response.status >= 400 && error.response.status < 500) { throw error; } if (attempt === retries) { throw error; // Se agotaron todos los intentos } const delay = baseDelay * Math.pow(2, attempt - 1) + Math.random() * 1000; console.log(`Intento ${attempt} falló. Esperando ${delay}ms...`); await sleep(delay); } }}

// Usoasync function fetchData() { const response = await withRetry(() => axios.get('https://api.example.com/data'), 5, 500); return response.data;}

fetchData().then(console.log).catch(console.error);


Puntos clave: excluir errores 4xx (errores del cliente) de los reintentos, usar retroceso exponencial con jitter para evitar tormentas de reintentos, y establecer un límite máximo de intentos.

Blogs

Book Recommendations