La documentación de las API y las herramientas de desarrollador del navegador suelen dar el ejemplo de solicitud como un comando cURL. Es ideal para una prueba rápida en la terminal, pero cuando necesitas llevar esa misma solicitud a una aplicación Node.js o a un script de scraping, tienes que traducir el comando a JavaScript. En este artículo explicamos los equivalentes en JavaScript de las opciones de cURL más usadas, ejemplos de conversión, cuatro formas de usar un proxy en Node.js, la gestión de errores y los errores más habituales.
Todos los ejemplos se han probado con Node.js 24, undici 8 y Axios 1.20.
¿Cuál es el equivalente de cURL en JavaScript?
Hay dos opciones principales:
- fetch: viene integrado en los navegadores y, desde Node.js 18, también en Node.js. No requiere paquetes adicionales. La implementación de fetch de Node.js usa por debajo la librería undici.
- Axios: una librería popular usada desde hace tiempo, que convierte a JSON de forma automática y ofrece soporte de interceptores de solicitud y respuesta.
También es posible reutilizar un comando cURL sin salir del código (con child_process), pero este método complica la gestión de errores y la portabilidad, y depende de que cURL esté instalado en la máquina. Casi siempre es mejor escribirlo con un cliente nativo.
Equivalentes en JavaScript de las opciones de cURL
| cURL | fetch | Axios |
|---|---|---|
curl URL | fetch(url) | axios.get(url) |
-X POST | method: "POST" | axios.post(url, datos) |
-H "Nombre: Valor" | headers: { Nombre: "Valor" } | headers: { Nombre: "Valor" } |
-d '{"a":1}' | body: JSON.stringify({ a: 1 }) | objeto como segundo parámetro |
-d "a=1&b=2" | body: new URLSearchParams({ a: 1, b: 2 }) | new URLSearchParams(...) como segundo parámetro |
-F "archivo=@a.png" | body: FormData | FormData como segundo parámetro |
-u usuario:contraseña | cabecera Authorization: "Basic ..." | auth: { username, password } |
-b "nombre=valor" (cookie) | headers: { Cookie: "nombre=valor" } | headers: { Cookie: "nombre=valor" } |
-A "UA" | headers: { "User-Agent": "UA" } | headers: { "User-Agent": "UA" } |
-L (seguir redirecciones) | Las sigue por defecto | Las sigue por defecto |
-x proxy | dispatcher: new ProxyAgent(...) | httpsAgent o proxy |
-m 20 (tiempo de espera) | signal: AbortSignal.timeout(20000) | timeout: 20000 |
-i (cabeceras de respuesta) | response.headers | response.headers |
-o archivo | escribe el cuerpo con fs.writeFile | responseType: "stream" |
Ejemplo: traducir una solicitud POST
Tomemos el siguiente comando cURL:
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-d '{"q": 1}'Con fetch
const response = await fetch("https://httpbin.org/post", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer TOKEN",
},
body: JSON.stringify({ q: 1 }),
signal: AbortSignal.timeout(20000),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
console.log(data.json);fetch tiene una particularidad: cuando el servidor devuelve 404 o 500, no lanza un error. Debes comprobar tú mismo si la respuesta fue exitosa con response.ok. Un error de red (DNS, conexión rechazada, tiempo de espera) sí llega como una excepción.
Con Axios
npm install axiosimport axios from "axios";
const { data } = await axios.post(
"https://httpbin.org/post",
{ q: 1 },
{ headers: { Authorization: "Bearer TOKEN" }, timeout: 20000 },
);
console.log(data.json);Axios convierte el objeto a JSON por sí mismo, añade la cabecera Content-Type y lanza un error en las respuestas que no sean 2xx.
Ejemplo: envío de formularios y subida de archivos
Un envío de formulario con el formato -d "a=1&b=2" de cURL se corresponde en JavaScript con URLSearchParams:
const response = await fetch("https://httpbin.org/post", {
method: "POST",
body: new URLSearchParams({ a: "1", b: "2" }),
});
console.log((await response.json()).form);Cuando se le pasa un cuerpo URLSearchParams, fetch establece por sí mismo la cabecera Content-Type como application/x-www-form-urlencoded. Para la subida de archivos (-F) se usa FormData, y el valor del límite (boundary) también se añade de forma automática. Un detalle: si importas la función fetch del paquete undici, importa también la clase FormData del mismo paquete; el FormData integrado no combina bien con el fetch de undici, y el cuerpo se envía como texto plano.
import { openAsBlob } from "node:fs";
const form = new FormData();
form.append("dosya", await openAsBlob("./a.png"), "a.png");
const response = await fetch("https://httpbin.org/post", { method: "POST", body: form });
console.log(Object.keys((await response.json()).files));Cuatro formas de usar un proxy en Node.js
En cURL, el proxy es una única opción -x. En Node.js, el método cambia según el cliente que uses.
1. fetch y el ProxyAgent de undici
npm install undiciimport { fetch, ProxyAgent } from "undici";
const dispatcher = new ProxyAgent("http://kullanici:parola@pr.proxynet.io:8000");
const response = await fetch("https://httpbin.org/ip", { dispatcher });
console.log(await response.text());Fíjate en que también importamos la función fetch del paquete undici. El fetch integrado de Node.js también está basado en undici, pero como el paquete instalado por separado puede tener una versión distinta, tomar ambos del mismo paquete es lo más seguro.
2. Ajuste global para todas las llamadas fetch
Si quieres que todas las llamadas fetch de la aplicación usen el mismo proxy, puedes definir un dispatcher global:
import { ProxyAgent, setGlobalDispatcher } from "undici";
setGlobalDispatcher(new ProxyAgent("http://kullanici:parola@pr.proxynet.io:8000"));
const response = await fetch("https://httpbin.org/ip");
console.log(await response.text());Este método también cubre las llamadas fetch que se hacen desde dentro de una librería de terceros; así haces pasar su tráfico por el proxy sin tocar el código fuente de la librería.
3. Con una variable de entorno, sin tocar el código
Las versiones recientes de Node.js pueden leer las variables de entorno estándar de proxy para el fetch integrado cuando se activa la variable NODE_USE_ENV_PROXY:
NODE_USE_ENV_PROXY=1 HTTPS_PROXY="http://kullanici:parola@pr.proxynet.io:8000" node uygulama.mjsEste método es útil para hacer pasar un script existente por el proxy sin tocar el código fuente. También se lee la variable NO_PROXY; puedes añadir a esta lista las direcciones de tu red interna para mantenerlas fuera del proxy. Comprueba en la documentación de Node.js si tu versión incluye esta función.
4. Axios y destinos HTTPS
La opción proxy integrada de Axios funciona sin problemas para direcciones HTTP sin cifrar:
const { data } = await axios.get("http://httpbin.org/ip", {
proxy: {
protocol: "http",
host: "pr.proxynet.io",
port: 8000,
auth: { username: "kullanici", password: "parola" },
},
});Para direcciones HTTPS, en cambio, es más fiable usar un agente de proxy:
npm install https-proxy-agentimport axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";
const httpsAgent = new HttpsProxyAgent("http://kullanici:parola@pr.proxynet.io:8000");
const { data } = await axios.get("https://httpbin.org/ip", { httpsAgent, proxy: false });
console.log(data);La línea proxy: false es importante: desactiva la lógica de proxy propia de Axios y deja el túnel entero en manos del agente. Si creas el agente una sola vez y lo asocias a una instancia con axios.create({ httpsAgent, proxy: false }), no tienes que repetirlo en cada llamada.
Comparación de los cuatro métodos
| Método | Alcance | Cambio de código | Cuándo conviene |
|---|---|---|---|
ProxyAgent de undici | Una sola llamada | dispatcher en cada llamada | Proxy distinto para solicitudes distintas |
setGlobalDispatcher | Todas las llamadas fetch | Una línea, al inicio | Un único proxy para toda la aplicación |
NODE_USE_ENV_PROXY | Todas las llamadas fetch integradas | Ninguno | Script existente, entorno de despliegue |
| Axios + agente | Instancia de Axios | httpsAgent + proxy: false | Cuando el proyecto ya usa Axios |
Reintentos y concurrencia
En un script de scraping real, las solicitudes a veces fallan: el proxy puede no llegar al destino o el sitio puede devolver un 429. La siguiente función auxiliar reintenta la solicitud fallida con espera exponencial y limita cuántas solicitudes están abiertas al mismo tiempo:
import { fetch, ProxyAgent } from "undici";
const dispatcher = new ProxyAgent("http://kullanici:parola@pr.proxynet.io:8000");
async function getir(url, deneme = 3) {
for (let i = 0; i < deneme; i++) {
try {
const r = await fetch(url, { dispatcher, signal: AbortSignal.timeout(20000) });
if (r.status === 429 || r.status >= 500) throw new Error(`HTTP ${r.status}`);
return await r.text();
} catch (hata) {
if (i === deneme - 1) throw hata;
await new Promise((coz) => setTimeout(coz, 1000 * 2 ** i));
}
}
}
async function topluGetir(urls, esZamanli = 5) {
const sonuclar = [];
for (let i = 0; i < urls.length; i += esZamanli) {
const grup = urls.slice(i, i + esZamanli);
sonuclar.push(...(await Promise.allSettled(grup.map(getir))));
}
return sonuclar;
}
const urls = Array.from({ length: 20 }, (_, i) => `https://httpbin.org/get?i=${i}`);
const sonuclar = await topluGetir(urls);
console.log(sonuclar.filter((s) => s.status === "fulfilled").length, "başarılı");Promise.allSettled evita que el error de una solicitud tire abajo todo el grupo; puedes examinar cada resultado por separado. El tiempo de espera se duplica en cada intento; insistir con un intervalo fijo solo hace que un sitio con límite de velocidad se vuelva aún más estricto.
Errores frecuentes
Caracteres especiales en la contraseña
Si la contraseña de tu proxy contiene caracteres como @, : o /, codifícalos dentro de la dirección con encodeURIComponent. De lo contrario, la dirección se interpreta mal y recibirás un error «407 Proxy Authentication Required».
Solicitudes sin tiempo de espera
Con la configuración por defecto, una solicitud puede quedarse esperando respuesta durante mucho tiempo y tu script se bloquea. Pon un límite de tiempo a cada solicitud: AbortSignal.timeout en fetch, timeout en Axios.
Demasiadas solicitudes a la vez
Lanzar cientos de solicitudes a la vez con Promise.all fuerza tus propios límites de conexión y hace que te topes con el límite de velocidad del sitio de destino. Limita la concurrencia en grupos pequeños, como en el ejemplo anterior. En trabajos que envían muchas solicitudes, un Proxies rotativos reduce el riesgo de bloqueo repartiendo el tráfico entre distintas IP.
Pasar agent al fetch integrado
En la antigua API http.request de Node.js, el proxy se pasa con la opción agent. El fetch integrado no reconoce esta opción y la ignora en silencio; la solicitud sale sin proxy. La clave correcta para fetch es dispatcher.
Copiar tal cual las cabeceras del navegador
Las solicitudes copiadas de las herramientas de desarrollador llevan cabeceras propias del navegador, como sec-ch-ua o sec-fetch-*. En Node.js no hace falta enviarlas, y algunas pueden interpretarse como una incoherencia en el sitio de destino; deja solo las cabeceras que realmente necesites.
Páginas para las que no basta con una solicitud HTTP
fetch y Axios solo obtienen el HTML que devuelve el servidor. Si el contenido se carga después con JavaScript en el navegador, no encontrarás lo que buscas en la página. En ese caso, o localizas la solicitud a la API que ocurre en segundo plano, o usas automatización de navegador. Comparamos las opciones en nuestro artículo Web scraping: ¿JavaScript o Python?; mostramos el uso de proxy con Puppeteer en Puppeteer y CAPTCHA.
¿fetch o Axios?
- Elige fetch si no quieres una dependencia adicional, si el código debe funcionar tanto en el navegador como en Node.js, o si quieres aprovechar las funciones de gestión de conexiones de undici.
- Elige Axios si necesitas comodidades como la conversión automática a JSON, los interceptores y el lanzamiento de errores en respuestas que no son 2xx, o si el proyecto ya lo usa.
La diferencia entre ambos es más una cuestión de costumbre que de funcionalidad. Empezar un proyecto nuevo con fetch mantiene bajo el número de dependencias; en un proyecto grande que ya usa Axios no hay motivo para cambiar a fetch.
Preguntas frecuentes
¿Puedo convertir automáticamente un comando cURL a JavaScript?
En la pestaña de Red de las herramientas de desarrollador del navegador, puedes hacer clic derecho sobre una solicitud y copiarla directamente como código fetch con la opción «Copy as fetch». Aun así, revisa el código resultante; las cabeceras propias del navegador pueden ser innecesarias en Node.js.
¿Puedo usar un proxy SOCKS5 en Node.js?
El fetch integrado y el ProxyAgent de undici son para proxies HTTP. Para SOCKS5 necesitas definir un agente con un paquete adicional, como socks-proxy-agent. En cURL, en cambio, el esquema socks5h:// es compatible de forma directa; puedes ver los detalles en nuestro artículo ¿Cómo usar un proxy con cURL?.
¿Se puede definir un proxy en el JavaScript que corre en el navegador?
No. El fetch del navegador no te permite cambiar el ajuste de proxy desde el código; el proxy se define en la configuración del navegador o del sistema operativo. Gestionar el proxy desde el código es propio de entornos de servidor como Node.js. Para el proxy a nivel de navegador se usan extensiones como SwitchyOmega.
¿Cómo conservo las cookies entre solicitudes?
El fetch integrado no guarda cookies. Tienes que leer la cabecera Set-Cookie de la respuesta y añadirla a la siguiente solicitud como cabecera Cookie; o bien usar con Axios un almacén de cookies basado en tough-cookie. No olvides que en los flujos con inicio de sesión la IP también debe mantenerse fija; el Proxies de sesión fija está diseñado para esto.
¿Puedo usar un proxy distinto en cada solicitud?
Sí. Con undici puedes dar un ProxyAgent distinto a cada llamada. Pero en lugar de gestionar tú mismo la lista, es más sencillo usar un proxy rotativo: una sola dirección, con una IP de salida distinta en cada conexión.
¿El mismo código funciona en TypeScript?
Sí. undici y Axios incluyen definiciones de tipos; para la opción dispatcher hay que usar el propio tipo fetch de undici, porque la definición de tipos del fetch integrado no reconoce esa opción.
En resumen
Los comandos cURL tienen un equivalente directo en JavaScript con fetch o Axios. En Node.js hay cuatro formas de usar un proxy: el ProxyAgent de undici, un dispatcher global, la variable de entorno NODE_USE_ENV_PROXY y un agente de proxy para Axios. Añadir desde el principio tiempo de espera, control de errores, reintentos con espera exponencial y un límite de concurrencia hace tus scripts mucho más fiables. Puedes encontrar los planes adecuados para tu tráfico web en nuestra página de Proxies HTTPS.




