Cliente HTTP en JavaScript: haciendo solicitudes correctamente en Node.js
El desarrollo web moderno es impensable sin la interacción con servidores remotos. Ya sea para obtener datos de una API REST, enviar formularios o cargar archivos, las solicitudes HTTP están en el núcleo de cualquier integración. Si estás desarrollando en Node.js, tienes varias herramientas potentes para crear un cliente HTTP. En este artículo, analizaremos cómo hacer solicitudes correctamente: desde módulos integrados hasta bibliotecas populares, con énfasis en el manejo de errores, tiempos de espera y legibilidad del código.
Veremos tres enfoques principales: el módulo integrado http/https, el moderno fetch (disponible en Node.js 18+), y la legendaria biblioteca axios. Para cada enfoque, encontrarás ejemplos prácticos y recomendaciones.
1. Módulo integrado http/https: un enfoque clásico
Node.js ha incluido los módulos http y https para trabajar con solicitudes de red desde sus inicios. Este es el método de más bajo nivel, que te da control total sobre el proceso. Sin embargo, requiere más código para manejar la respuesta.
Ejemplo de una solicitud GET usando el módulo https:
const https = require('https');
function getUsers() { return new Promise((resolve, reject) => { const options = { hostname: 'jsonplaceholder.typicode.com', path: '/users', method: 'GET', headers: { 'Content-Type': 'application/json' } };
const req = https.request(options, (res) => { let data = '';
// Collect data in chunks res.on('data', (chunk) => { data += chunk; });
// When the response is fully received res.on('end', () => { if (res.statusCode >= 200 && res.statusCode < 300) { resolve(JSON.parse(data)); } else { reject(new Error(`HTTP Error: ${res.statusCode}`)); } }); });
// Handle network errors req.on('error', (err) => { reject(err); });
// Timeout in case of hanging req.setTimeout(5000, () => { req.destroy(); reject(new Error('Request timeout')); });
req.end(); });}
// UsagegetUsers() .then(users => console.log('Users:', users.length)) .catch(err => console.error('Error:', err.message));Ventajas:
- No es necesario instalar paquetes adicionales.
- Control total sobre cada etapa de la conexión.
Desventajas:
- Mucho código repetitivo.
- Manejo manual de fragmentos y ensamblaje del cuerpo de la respuesta.
- Trabajo incómodo con promesas sin un envoltorio.
Este enfoque está justificado si estás escribiendo una biblioteca para operaciones de red o necesitas el máximo rendimiento sin dependencias adicionales. En otros casos, es mejor usar soluciones de mayor nivel.
2. Fetch API: el estándar moderno
A partir de Node.js 18, fetch se convirtió en una función integrada, convirtiéndola en la herramienta principal para solicitudes HTTP. Está basada en promesas y tiene una sintaxis limpia e intuitiva.
Ejemplo de una solicitud POST con fetch:
async function createPost() { const url = 'https://jsonplaceholder.typicode.com/posts'; const newPost = { title: 'HTTP client in JavaScript', body: 'Making requests correctly', userId: 1 };
try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newPost) });
// Check the response status if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); }
const data = await response.json(); console.log('Created post ID:', data.id); return data; } catch (error) {