cURL em JavaScript: fetch, Axios e proxy

Publicado:

11 min de leitura

Acar Diveroli
Autor: Acar Diveroli
Ícone de prompt de comando entre chaves

Documentações de API e as ferramentas de desenvolvedor do navegador costumam apresentar a requisição de exemplo como um comando cURL. É ideal para um teste rápido no terminal; mas quando você precisa levar a mesma requisição para uma aplicação Node.js ou para um script de coleta de dados, precisa converter o comando para JavaScript. Neste artigo explicamos os equivalentes em JavaScript das opções mais usadas do cURL, exemplos de conversão, quatro formas de usar proxy no Node.js, tratamento de erros e os erros mais comuns.

Todos os exemplos foram testados no Node.js 24, com undici 8 e Axios 1.20.

Qual é o equivalente do cURL em JavaScript?

Existem duas opções principais:

  • fetch: vem embutido nos navegadores e, desde o Node.js 18, também no Node.js. Não exige pacote adicional. A implementação de fetch do Node.js usa a biblioteca undici por baixo dos panos.
  • Axios: biblioteca popular, usada há muito tempo, que converte JSON automaticamente e oferece suporte a interceptadores de requisição e resposta.

Também é possível reutilizar um comando cURL sem sair do terminal (via child_process), mas esse método dificulta o tratamento de erros e a portabilidade; você fica dependente de o cURL estar instalado na máquina. Escrever com um cliente nativo é quase sempre melhor.

Equivalentes em JavaScript das opções do cURL

cURLfetchAxios
curl URLfetch(url)axios.get(url)
-X POSTmethod: "POST"axios.post(url, dados)
-H "Nome: Valor"headers: { Nome: "Valor" }headers: { Nome: "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 "arquivo=@a.png"body: FormDataFormData como segundo parâmetro
-u usuario:senhacabeçalho Authorization: "Basic ..."auth: { username, password }
-b "nome=valor" (cookie)headers: { Cookie: "nome=valor" }headers: { Cookie: "nome=valor" }
-A "UA"headers: { "User-Agent": "UA" }headers: { "User-Agent": "UA" }
-L (segue redirecionamento)Segue por padrãoSegue por padrão
-x proxydispatcher: new ProxyAgent(...)httpsAgent ou proxy
-m 20 (tempo limite)signal: AbortSignal.timeout(20000)timeout: 20000
-i (cabeçalhos de resposta)response.headersresponse.headers
-o arquivoescreva o corpo com fs.writeFileresponseType: "stream"

Exemplo: convertendo uma requisição POST

Vamos considerar o seguinte comando cURL:

bash
curl -X POST https://httpbin.org/post \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer TOKEN" \
  -d '{"q": 1}'

Com fetch

javascript
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);

O fetch tem uma particularidade: quando o servidor retorna 404 ou 500, ele não lança erro. Você mesmo precisa checar se a resposta foi bem-sucedida usando response.ok. Já um erro de rede (DNS, conexão recusada, tempo limite) chega como exceção.

Com Axios

bash
npm install axios
javascript
import axios from "axios";

const { data } = await axios.post(
  "https://httpbin.org/post",
  { q: 1 },
  { headers: { Authorization: "Bearer TOKEN" }, timeout: 20000 },
);

console.log(data.json);

O Axios converte o objeto para JSON automaticamente, adiciona o cabeçalho Content-Type e lança erro em respostas fora da faixa 2xx.

Exemplo: envio de formulário e upload de arquivo

Um envio de formulário no formato -d "a=1&b=2" do cURL é atendido em JavaScript com URLSearchParams:

javascript
const response = await fetch("https://httpbin.org/post", {
  method: "POST",
  body: new URLSearchParams({ a: "1", b: "2" }),
});
console.log((await response.json()).form);

Quando um corpo URLSearchParams é passado, o fetch define sozinho o cabeçalho Content-Type como application/x-www-form-urlencoded. Para upload de arquivo (-F), usa-se FormData, e o valor de fronteira (boundary) também é adicionado automaticamente. Um detalhe: se você importa a função fetch do pacote undici, importe também a classe FormData do mesmo pacote; o FormData embutido não combina com o fetch do undici, e o corpo vai como texto puro.

javascript
import { openAsBlob } from "node:fs";

const form = new FormData();
form.append("arquivo", 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));

Quatro formas de usar proxy no Node.js

No cURL, o proxy é uma única opção, -x. No Node.js, o método muda de acordo com o cliente que você usa.

1. fetch e o ProxyAgent do undici

bash
npm install undici
javascript
import { 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());

Note que também importamos a função fetch do pacote undici. O fetch embutido do Node.js também é baseado no undici, mas, como pode haver diferença de versão no pacote instalado separadamente, importar os dois do mesmo pacote é o caminho mais seguro.

2. Configuração global para todas as chamadas fetch

Se você quer que toda chamada fetch da aplicação use o mesmo proxy, pode definir um dispatcher global:

javascript
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());

Esse método também cobre chamadas fetch feitas dentro de uma biblioteca de terceiros; você passa o tráfego dela pelo proxy sem tocar no código-fonte da biblioteca.

3. Por variável de ambiente, sem alterar código

Versões recentes do Node.js conseguem ler as variáveis de ambiente padrão de proxy para o fetch embutido quando a variável NODE_USE_ENV_PROXY está ativada:

bash
NODE_USE_ENV_PROXY=1 HTTPS_PROXY="http://kullanici:parola@pr.proxynet.io:8000" node aplicacao.mjs

Esse método é útil para passar um script já existente pelo proxy sem tocar no código-fonte. A variável NO_PROXY também é lida; você pode adicionar endereços de rede interna a essa lista para mantê-los fora do proxy. Verifique na documentação do Node.js se esse recurso está disponível na sua versão.

4. Axios e destinos HTTPS

A opção proxy embutida do Axios funciona sem problemas para endereços HTTP sem criptografia:

javascript
const { data } = await axios.get("http://httpbin.org/ip", {
  proxy: {
    protocol: "http",
    host: "pr.proxynet.io",
    port: 8000,
    auth: { username: "kullanici", password: "parola" },
  },
});

Para endereços HTTPS, é mais confiável usar um agente de proxy:

bash
npm install https-proxy-agent
javascript
import 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);

A linha proxy: false é importante; ela desliga a própria lógica de proxy do Axios e deixa o túnel totalmente a cargo do agente. Se você criar o agente uma vez e vinculá-lo a uma instância com axios.create({ httpsAgent, proxy: false }), não precisa reescrevê-lo em cada chamada.

Comparando os quatro métodos

MétodoEscopoAlteração de códigoSituação em que é indicado
ProxyAgent do undiciUma única chamadadispatcher em cada chamadaProxy diferente para requisições diferentes
setGlobalDispatcherTodas as chamadas fetchUma linha, no inícioUm único proxy para toda a aplicação
NODE_USE_ENV_PROXYTodas as chamadas do fetch embutidoNenhumaScript existente, ambiente de implantação
Axios + agenteInstância do AxioshttpsAgent + proxy: falseQuando o projeto já usa Axios

Nova tentativa e concorrência

Em um script real de coleta de dados, as requisições às vezes falham; o proxy pode não alcançar o destino, ou o site pode retornar 429. A função auxiliar abaixo tenta novamente a requisição com falha usando espera exponencial e limita o número de requisições abertas ao mesmo tempo:

javascript
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, "com sucesso");

O Promise.allSettled evita que o erro de uma requisição derrube todo o grupo; você pode examinar cada resultado individualmente. O tempo de espera dobra a cada tentativa; insistir em intervalos fixos pode fazer um site com limite de taxa aplicar restrições ainda mais rígidas.

Erros comuns

Caractere especial na senha

Se a sua senha de proxy tiver caracteres como @, : ou /, codifique-os dentro do endereço com encodeURIComponent. Caso contrário, o endereço é interpretado errado e você recebe o erro "407 Proxy Authentication Required".

Requisições sem tempo limite

Nas configurações padrão, uma requisição pode esperar resposta por muito tempo e travar o seu script. Coloque um limite de tempo em cada requisição com AbortSignal.timeout no fetch e timeout no Axios.

Muitas requisições ao mesmo tempo

Disparar centenas de requisições de uma vez com Promise.all sobrecarrega tanto os seus próprios limites de conexão quanto faz você esbarrar em limite de taxa no site de destino. Limite a concorrência em pequenos grupos, como no exemplo acima. Em trabalhos com muitas requisições, o Proxies rotativos distribui o tráfego entre IPs diferentes e reduz o risco de bloqueio.

Passar agent para o fetch embutido

Na antiga API http.request do Node.js, o proxy é passado pela opção agent. O fetch embutido não reconhece essa opção e a ignora silenciosamente; a requisição sai sem proxy. Para o fetch, a chave correta é dispatcher.

Copiar cabeçalhos do navegador tal como estão

Requisições copiadas das ferramentas de desenvolvedor carregam cabeçalhos específicos do navegador, como sec-ch-ua e sec-fetch-*. No Node.js não é necessário enviá-los, e alguns podem ser avaliados como uma inconsistência pelo site de destino; deixe apenas os cabeçalhos realmente necessários.

Páginas em que a requisição HTTP não basta

O fetch e o Axios só obtêm o HTML retornado pelo servidor. Se o conteúdo é carregado depois, no navegador, com JavaScript, você não vai encontrar o que procura na página. Nesse caso, você precisa encontrar a requisição de API que roda em segundo plano ou usar uma automação de navegador. Comparamos as opções no artigo Extração de dados: JavaScript ou Python?; mostramos o uso de proxy com o Puppeteer no artigo Puppeteer e CAPTCHA.

fetch ou Axios?

  • Escolha fetch se você não quer uma dependência adicional, se o código vai rodar tanto no navegador quanto no Node.js, ou se quer aproveitar os recursos de gerenciamento de conexão do undici.
  • Escolha Axios se você precisa de conveniências como conversão automática de JSON, interceptadores e erro automático em respostas fora do 2xx, ou se o projeto já o usa.

A diferença entre os dois está mais no hábito do que na função. Começar um projeto novo com fetch mantém o número de dependências baixo; em um projeto grande já feito com Axios, não há motivo para migrar para o fetch.

Perguntas frequentes

Posso converter um comando cURL para JavaScript automaticamente?

Nas ferramentas de desenvolvedor do navegador, na aba Rede, você pode clicar com o botão direito em uma requisição e usar a opção "Copy as fetch" para copiar diretamente como código fetch. Ainda assim, revise o código gerado; cabeçalhos específicos do navegador podem ser desnecessários no Node.js.

Posso usar proxy SOCKS5 no Node.js?

O fetch embutido e o ProxyAgent do undici são para proxy HTTP. Para SOCKS5, você precisa definir um agente com um pacote adicional, como socks-proxy-agent. No lado do cURL, o esquema socks5h:// já é suportado diretamente; os detalhes estão no artigo Como usar proxy com o cURL?.

Dá para definir proxy em JavaScript rodando no navegador?

Não. O fetch do navegador não permite alterar a configuração de proxy por código; o proxy é definido nas configurações do navegador ou do sistema operacional. Gerenciar o proxy a partir do código é algo específico de ambientes de servidor, como o Node.js. Para proxy no nível do navegador, são usadas extensões como o SwitchyOmega.

Como mantenho cookies entre requisições?

O fetch embutido não guarda cookies. Você precisa ler o cabeçalho Set-Cookie da resposta e adicioná-lo como cabeçalho Cookie na requisição seguinte; ou pode usar um cookie jar baseado em tough-cookie com o Axios. Não esqueça que, em fluxos com login, o IP também precisa se manter fixo; o Proxies de sessão fixa foi feito para isso.

Posso usar um proxy diferente em cada requisição?

Sim. Com o undici, você pode passar um ProxyAgent diferente em cada chamada. Mas, em vez de gerenciar a lista você mesmo, usar um proxy rotativo é mais simples: um único endereço, um IP de saída diferente em cada conexão.

O mesmo código funciona em TypeScript?

Sim. O undici e o Axios vêm com definições de tipo; para a opção dispatcher, é preciso usar o próprio tipo fetch do undici, porque a definição de tipo do fetch embutido não conhece essa opção.

Em resumo

Comandos cURL podem ser atendidos em JavaScript, ponto a ponto, com fetch ou Axios. No Node.js, existem quatro caminhos para proxy: o ProxyAgent do undici, o dispatcher global, a variável de ambiente NODE_USE_ENV_PROXY e o agente de proxy do Axios. Adicionar desde o início o tempo limite, o tratamento de erro, a nova tentativa com espera exponencial e o limite de concorrência torna os seus scripts muito mais confiáveis. Você encontra os pacotes adequados para tráfego web na nossa página de Proxies HTTPS.