Axios ou fetch: consumir API no Node e no navegador
O que o axios faz que o fetch não faz, o que virou dispensável no Node 24, e os mesmos seis casos escritos nas duas bibliotecas.
fetch e axios são clientes HTTP: ferramentas usadas pelo seu código para
enviar uma requisição e receber a resposta de uma API. O fetch já vem no
navegador e nas versões atuais do Node; axios é uma dependência instalada no
projeto e acrescenta conveniências como interceptores, rejeição automática para
status de erro e progresso de upload.
Pense em duas formas de enviar o mesmo pacote. Com fetch, você fala direto com
a transportadora e confere por conta própria o protocolo, o conteúdo e os
problemas da entrega. Com axios, há uma central que abre o pacote de resposta,
aplica regras antes e depois da viagem e transforma certos status em erro. A
estrada é a mesma: tecnicamente, ambos fazem HTTP. A diferença está no trabalho
que cada API assume ao redor da requisição.
Para não discutir no abstrato, este artigo roda as duas bibliotecas contra a
mesma API: a clínica veterinária Pata Amiga, um servidor Express de verdade
ouvindo em localhost:4477, com rotas que respondem 200, 201, 400, 401, 404,
503, uma rota que demora três segundos de propósito e uma rota que devolve HTML
onde você esperava JSON. Cada saída que você vê abaixo saiu do terminal, no
Node 24.16.0 com axios 1.19.0 e Express 5.2.1.
O fetch que já vem no Node 24 e no navegador
fetch é uma função global. Não tem import, não tem npm install, e o mesmo
código roda nos dois lados:
console.log('fetch ->', typeof fetch);
console.log('Headers ->', typeof Headers);
console.log('FormData ->', typeof FormData);
console.log('AbortSignal.timeout ->', typeof AbortSignal.timeout);
console.log('Node ->', process.version);Isso é novo o suficiente para ainda confundir muita gente. O fetch entrou como
global no Node 18, deixou de ser experimental no Node 21 e, no Node 24, roda sem
imprimir nenhum aviso no terminal. Se o seu projeto ainda tem node-fetch no
package.json e o Node é 20 ou mais novo, você está instalando um pacote para
ter algo que já está lá.
A diferença de forma entre os dois aparece na primeira linha de código. O fetch
entrega a resposta; o corpo vem numa segunda etapa, porque ele pode ser um
JSON, um texto, um blob ou um stream de vários megabytes que você talvez nem
queira carregar na memória:
import axios from 'axios';
// fetch: duas etapas — a resposta chega, o corpo vem depois
const resposta = await fetch('http://localhost:4477/pacientes/17');
const comFetch = await resposta.json();
console.log('fetch ->', comFetch);
// axios: uma etapa — o corpo já vem em .data, com o JSON convertido
const { data: comAxios } = await axios.get('http://localhost:4477/pacientes/17');
console.log('axios ->', comAxios);
console.log('chaves do retorno do axios:', Object.keys(await axios.get('http://localhost:4477/pacientes/17')));O objeto do paciente é idêntico. O que muda é o caminho até ele:
O axios devolve um envelope com seis chaves e o corpo já convertido dentro de
data. É uma etapa a menos por chamada — e, multiplicado por cinquenta chamadas
num projeto, é o argumento honesto de quem prefere o axios por ergonomia.
JSON, erro HTTP e timeout: os três atritos do fetch puro
Se o fetch cobre tudo, por que tanta gente ainda instala o axios? Porque o
fetch tem três comportamentos que só mordem em produção. Vale ver os três
rodando.
O 404 não cai no catch
Esta é a armadilha número um. O paciente 99 não existe na clínica. O código
abaixo tem try/catch e mesmo assim segue em frente:
try {
const resposta = await fetch('http://localhost:4477/pacientes/99');
const paciente = await resposta.json();
console.log('nome do paciente:', paciente.nome);
} catch (erro) {
console.log('caiu no catch:', erro.message);
}O catch nunca rodou. O servidor respondeu 404 com o corpo
{ erro: 'paciente_nao_encontrado' }, o fetch considerou isso uma requisição
bem-sucedida — porque, do ponto de vista da rede, foi — e o .json() converteu
o corpo do erro num objeto que não tem nome. Daí o undefined que segue vivo
pela aplicação até estourar três funções adiante.
O axios rejeita a promessa em qualquer status fora da faixa 2xx, e ainda anexa a resposta do servidor ao erro:
import axios from 'axios';
try {
const resposta = await axios.get('http://localhost:4477/pacientes/99');
console.log('nome do paciente:', resposta.data.nome);
} catch (erro) {
console.log('caiu no catch:', erro.message);
console.log('status:', erro.response.status);
console.log('corpo do erro:', erro.response.data);
}Nem toda resposta 200 é JSON
A rota /status da clínica responde 200, mas devolve HTML. Chamar .json()
nela produz o erro mais famoso de quem consome API:
import axios from 'axios';
const { data, status } = await axios.get('http://localhost:4477/status');
console.log('axios ', status, typeof data, JSON.stringify(data));
const resposta = await fetch('http://localhost:4477/status');
console.log('fetch ', resposta.status, resposta.headers.get('content-type'));
await resposta.json();SyntaxError: Unexpected token ‘<’, “<h1>Clinic”… is not valid JSON at JSON.parse (<anonymous>) at parseJSONFromBytes (node:internal/deps/undici/undici:4337:19) at successSteps (node:internal/deps/undici/undici:6985:27) at readAllBytes (node:internal/deps/undici/undici:5908:13) at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
Node.js v24.16.0
Esse SyntaxError: Unexpected token é quase sempre isso: você pediu JSON e
recebeu uma página de erro do proxy, do Nginx ou do próprio framework. O token
inesperado é o sinal de menor que abre a tag h1, a primeira letra do HTML que
chegou no lugar do objeto. O axios não explode — ele
tenta converter, não consegue, e devolve a string crua em data. É mais gentil,
e também mais silencioso: seu código segue achando que tem um objeto.
Sem AbortSignal, o fetch espera para sempre
A rota /vacinas demora três segundos. O fetch não tem opção de timeout; o
corte vem de um AbortSignal. O axios tem uma opção e pronto:
import axios from 'axios';
const t1 = performance.now();
try {
await fetch('http://localhost:4477/vacinas', { signal: AbortSignal.timeout(800) });
} catch (erro) {
console.log('fetch ', erro.name, '|', erro.message, '|', Math.round(performance.now() - t1), 'ms');
}
const t2 = performance.now();
try {
await axios.get('http://localhost:4477/vacinas', { timeout: 800 });
} catch (erro) {
console.log('axios ', erro.code, '|', erro.message, '|', Math.round(performance.now() - t2), 'ms');
}Os dois cortaram no tempo certo. AbortSignal.timeout() não é nenhum bicho de
sete cabeças — mas é uma linha que você precisa lembrar de escrever em toda
chamada, e é justamente esse tipo de repetição que empurra times para o axios.
Os mesmos seis casos nas duas bibliotecas
Este é o resumo do que muda no dia a dia. Nenhuma linha aqui é opinião: é o que cada biblioteca exige para o mesmo resultado.
| caso | com fetch | com axios |
|---|---|---|
| GET com JSON | (await fetch(url)).json() |
(await axios.get(url)).data |
| POST com JSON | method, Content-Type e JSON.stringify na mão |
objeto direto no segundo argumento |
| erro HTTP | if (!resposta.ok) throw escrito por você |
rejeita sozinho, com erro.response |
| resposta que não é JSON | SyntaxError estourando |
devolve a string em data, sem erro |
| timeout | signal: AbortSignal.timeout(ms) |
timeout: ms |
| header fixo em toda chamada | um wrapper seu | axios.create({ headers }) |
O caso do POST merece o código, porque ele guarda o esquecimento mais comum de
quem está saindo do fetch de tutorial para o fetch de projeto:
import axios from 'axios';
// fetch: você monta o header e serializa o corpo na mão
const r1 = await fetch('http://localhost:4477/consultas', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ pacienteId: 17, motivo: 'vacina anual' }),
});
console.log('fetch ', r1.status, await r1.json());
// axios: o objeto vai direto; o header e o JSON.stringify são dele
const r2 = await axios.post('http://localhost:4477/consultas', {
pacienteId: 18,
motivo: 'corte de unha',
});
console.log('axios ', r2.status, r2.data);
// o esquecimento clássico: POST com fetch sem o Content-Type
const r3 = await fetch('http://localhost:4477/consultas', {
method: 'POST',
body: JSON.stringify({ pacienteId: 17, motivo: 'retorno' }),
});
console.log('sem header', r3.status, await r3.json());Repare na terceira linha. O corpo foi enviado, o JSON estava perfeito, e mesmo
assim a clínica respondeu 400 dizendo que faltou pacienteId. Faltou o
Content-Type: application/json: sem ele, o express.json() não processa o
corpo e req.body chega vazio. Esse é o bug que consome uma tarde inteira,
porque o erro aponta para o campo e não para o header. O axios manda o header
sozinho quando você passa um objeto, e por isso a classe de bug simplesmente não
existe lá.
O último caso da tabela é o header fixo. Com fetch, ele viaja em toda chamada;
com axios, você configura uma instância uma vez:
import axios from 'axios';
// fetch: o header vai na mão, em toda chamada
const r1 = await fetch('http://localhost:4477/prontuarios/17', {
headers: { Authorization: 'Bearer chave-da-clinica' },
});
console.log('fetch com token ', r1.status, await r1.json());
const r2 = await fetch('http://localhost:4477/prontuarios/17');
console.log('fetch sem token ', r2.status, await r2.json());
// axios: instância com o header configurado uma vez
const api = axios.create({
baseURL: 'http://localhost:4477',
headers: { Authorization: 'Bearer chave-da-clinica' },
timeout: 5000,
});
const r3 = await api.get('/prontuarios/17');
console.log('axios instância ', r3.status, r3.data);Se você já leu sobre autenticação com JWT no Express,
esse Authorization é exatamente o token que o login devolve.
Interceptor: o recurso que ainda justifica o axios
Aqui está o recurso para o qual o fetch não tem equivalente direto. Um
interceptor é um gancho que roda em toda requisição ou resposta da
instância, inclusive nas chamadas que já estão escritas em outros arquivos e que
você não vai tocar.
O caso clássico: o token venceu, a API respondeu 401, e você quer renovar o
token e repetir a chamada original sem que quem chamou perceba nada.
import axios from 'axios';
let token = 'token-vencido';
const api = axios.create({ baseURL: 'http://localhost:4477' });
// interceptor de requisição: carimba o token atual em toda chamada
api.interceptors.request.use((config) => {
config.headers.Authorization = `Bearer ${token}`;
console.log(' -> saindo:', config.method.toUpperCase(), config.url);
return config;
});
// interceptor de resposta: no 401, renova o token e repete a chamada original
api.interceptors.response.use(
(resposta) => resposta,
async (erro) => {
if (erro.response?.status === 401 && !erro.config._jaTentou) {
console.log(' <- 401: renovando o token');
const { data } = await axios.post('http://localhost:4477/login');
token = data.token;
erro.config._jaTentou = true;
return api.request(erro.config);
}
return Promise.reject(erro);
},
);
// quem chama não sabe de nada disso
const { data } = await api.get('/prontuarios/17');
console.log('prontuário:', data);A chamada saiu, tomou 401, o interceptor renovou o token, a chamada saiu de novo
e o await api.get() devolveu o prontuário. A última linha do arquivo não sabe
que isso aconteceu.
Dá para fazer o mesmo com fetch? Dá — desde que todo mundo passe pela sua
função. É o preço do interceptor caseiro: ele não é um gancho, é uma porta, e
funciona só enquanto ninguém chama fetch direto. Este é o cliente da clínica,
com timeout, baseURL, JSON automático, erro tipado e renovação de token:
const BASE = 'http://localhost:4477';
let token = 'token-vencido';
class ErroDeApi extends Error {
constructor(status, corpo) {
super(`A clínica respondeu ${status}`);
this.name = 'ErroDeApi';
this.status = status;
this.corpo = corpo;
}
}
async function api(caminho, opcoes = {}, jaTentou = false) {
const resposta = await fetch(BASE + caminho, {
...opcoes,
signal: AbortSignal.timeout(opcoes.timeout ?? 5000),
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
...opcoes.headers,
},
body: opcoes.json ? JSON.stringify(opcoes.json) : opcoes.body,
});
if (resposta.status === 401 && !jaTentou) {
const login = await fetch(`${BASE}/login`, { method: 'POST' });
token = (await login.json()).token;
return api(caminho, opcoes, true);
}
const tipo = resposta.headers.get('content-type') ?? '';
const corpo = tipo.includes('application/json') ? await resposta.json() : await resposta.text();
if (!resposta.ok) throw new ErroDeApi(resposta.status, corpo);
return corpo;
}
console.log('prontuário:', await api('/prontuarios/17'));
console.log('consulta :', await api('/consultas', { method: 'POST', json: { pacienteId: 17 } }));
console.log('status :', await api('/status'));
try {
await api('/pacientes/99');
} catch (erro) {
console.log('erro :', erro.name, erro.status, erro.corpo);
}Contando só linhas não vazias, o cliente com axios (instância mais dois
interceptores) fica em 19 linhas; o cliente com fetch que faz a mesma
coisa fica em 31 linhas. Doze linhas de diferença, escritas uma vez por
projeto. Se essa é a sua régua, o axios não se paga; se o seu projeto tem cinco
clientes de API diferentes e uma política de erro que muda toda semana, ele se
paga no primeiro mês.
Cancelamento, retry e upload com progresso
Cancelar é empate: os dois falam AbortController. A cena é a mesma de sempre —
o tutor digitou o nome do bicho, a busca saiu, e ele digitou de novo antes de a
primeira resposta chegar.
import axios from 'axios';
const controle = new AbortController();
setTimeout(() => controle.abort(), 300);
try {
await fetch('http://localhost:4477/vacinas', { signal: controle.signal });
} catch (erro) {
console.log('fetch:', erro.name, '|', erro.message);
}
const controle2 = new AbortController();
setTimeout(() => controle2.abort(), 300);
try {
await axios.get('http://localhost:4477/vacinas', { signal: controle2.signal });
} catch (erro) {
console.log('axios:', erro.code, '|', erro.message, '| é cancelamento?', axios.isCancel(erro));
}A vantagem miúda do axios é o axios.isCancel(): você distingue “cancelei de
propósito” de “deu erro” sem comparar string de mensagem. Com fetch, a
verificação é erro.name === 'AbortError'.
Já o retry expõe o atrito do fetch de novo. A rota /laboratorio/exames
responde 503 nas duas primeiras chamadas e só devolve o hemograma na terceira.
Um laço de retry comum não funciona com fetch cru:
const espera = (ms) => new Promise((r) => setTimeout(r, ms));
async function comRetry(chamada, tentativas = 4) {
for (let n = 1; n <= tentativas; n++) {
try {
return await chamada();
} catch (erro) {
if (n === tentativas) throw erro;
const atraso = 200 * 2 ** (n - 1);
console.log(` tentativa ${n} falhou; esperando ${atraso}ms`);
await espera(atraso);
}
}
}
// fetch cru: o 503 não vira exceção, então o retry nunca acontece
const resposta = await comRetry(() => fetch('http://localhost:4477/laboratorio/exames?corrida=cru'));
console.log('fetch cru ->', resposta.status, await resposta.json());
// com um empurrãozinho: transformar o status ruim em erro
async function pedir(url) {
const r = await fetch(url);
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
}
const dados = await comRetry(() => pedir('http://localhost:4477/laboratorio/exames?corrida=ok'));
console.log('fetch + throw ->', dados);A primeira linha é o bug em estado puro: a função de retry rodou, não imprimiu
nenhuma tentativa porque não viu exceção nenhuma, e devolveu o 503 de cara.
Só a segunda chamada, com o throw manual, insistiu até o laboratório
responder. Com axios, o mesmo laço funciona sem nenhuma adaptação, porque o
503 já chega como exceção:
import axios from 'axios';
const api = axios.create({ baseURL: 'http://localhost:4477' });
const t = performance.now();
const { data } = await comRetry(() => api.get('/laboratorio/exames?corrida=axios'));
console.log('axios ->', data, '|', Math.round(performance.now() - t), 'ms');Os 618 ms são os 200 ms mais os 400 ms de espera exponencial, mais o tempo das três requisições. Esse é o comportamento que você quer numa integração com laboratório externo: insistir com intervalo crescente, e não em rajada.
O upload com progresso é o último ponto, e nele o fetch simplesmente não
compete. Não existe evento de progresso de envio na especificação; a alternativa
é mandar o corpo como ReadableStream com duplex: 'half', o que exige suporte
do servidor e HTTP/2. Com axios são três linhas:
import axios from 'axios';
// 8 MB fingindo ser o raio-X do Tobias
const raioX = Buffer.alloc(8 * 1024 * 1024, 7);
const { data } = await axios.post('http://localhost:4477/exames/upload', raioX, {
headers: { 'Content-Type': 'application/octet-stream' },
onUploadProgress: (evento) => {
const pct = Math.round((evento.loaded / evento.total) * 100);
console.log(`enviado ${pct}% (${evento.loaded} de ${evento.total} bytes)`);
},
});
console.log('servidor recebeu:', data.recebido, 'bytes');Só dois eventos, e por um motivo interessante: em localhost o buffer do socket
esvazia praticamente de uma vez, então não há progresso intermediário para
relatar. Numa rede de verdade, com o tutor mandando o raio-X pelo 4G, esses
mesmos eventos chegam dezenas de vezes e a barra anda de fato.
Quando a rede cai: o erro que os dois escondem
Até aqui o servidor sempre respondeu alguma coisa. Falta o caso em que ele não
responde nada — servidor fora do ar, Wi-Fi que caiu, porta errada no .env.
Aqui, a porta 4488 não tem ninguém ouvindo:
import axios from 'axios';
try {
await fetch('http://localhost:4488/pacientes/17');
} catch (erro) {
console.log('fetch:', erro.name, '|', erro.message);
console.log(' causa:', erro.cause?.code, erro.cause?.message);
}
try {
await axios.get('http://localhost:4488/pacientes/17');
} catch (erro) {
console.log('axios:', erro.code, '|', erro.message);
console.log(' tem erro.response?', erro.response !== undefined);
}Duas coisas para guardar. A primeira: TypeError: fetch failed é a mensagem
mais inútil do Node, e o motivo real mora em erro.cause. Sempre que esse erro
aparecer no seu log, imprima a causa junto — é a diferença entre ECONNREFUSED,
ENOTFOUND e certificado inválido. Repare que a mensagem do axios é bem mais
informativa de graça: ela diz que tentou ::1 e 127.0.0.1, as duas famílias
de endereço do localhost.
A segunda é a última linha: tem erro.response? false. Quando não há resposta
nenhuma, erro.response é undefined — e o hábito que funcionou tão bem no 404
vira isto:
import axios from 'axios';
try {
await axios.get('http://localhost:4488/pacientes/17');
} catch (erro) {
// o hábito que funciona no 404 e explode quando a rede cai
console.log('status da falha:', erro.response.status);
}TypeError: Cannot read properties of undefined (reading ‘status’) at file:///private/tmp/pata-amiga/16-erro-response.mjs:7:49 at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
Node.js v24.16.0
É o Cannot read properties of undefined de sempre,
nascido dentro do próprio catch. A correção é uma interrogação:
erro.response?.status ?? 'sem resposta'. Escreva assim desde o primeiro dia —
esse erro só aparece quando a rede cai, ou seja, no pior momento possível.
Peso no bundle e o que isso custa no front
No servidor, o tamanho do axios é irrelevante. No navegador, ele é um custo real
que o fetch não tem, porque o fetch já está no aparelho de quem acessa.
Empacotei dois módulos que fazem a mesma busca de paciente — um com axios, outro
com fetch — e um terceiro com o cliente completo mostrado acima:
// com-axios.js
import axios from 'axios';
const api = axios.create({ baseURL: '/api', timeout: 5000 });
export async function buscarPaciente(id) {
const { data } = await api.get(`/pacientes/${id}`);
return data;
}npx esbuild com-axios.js --bundle --minify --format=esm --platform=browser --outfile=com-axios.min.js
gzip -9 -c com-axios.min.js > com-axios.min.js.gz
wc -c com-axios.min.js com-axios.min.js.gz| módulo | minificado | gzip |
|---|---|---|
| com axios | 48.328 B | 18.670 B |
com fetch puro |
198 B | 204 B |
com o cliente fetch completo |
527 B | 393 B |
18 KB compactados só para falar HTTP. Repare também que os 198 B do fetch puro
ficam maiores depois do gzip: o arquivo é tão pequeno que o cabeçalho da
compressão pesa mais que o ganho.
E o axios praticamente não encolhe com tree shaking. O bundle acima, que usa
exatamente dois recursos (create e get), ficou em 48.328 B — contra 48.485 B
do dist/esm/axios.min.js que o próprio pacote já publica pronto. O esbuild
conseguiu descartar 157 bytes. O motivo é a arquitetura: o axios é um objeto único com os
adaptadores dentro, não um punhado de funções independentes que o empacotador
consiga descartar.
E a velocidade? Medi 500 requisições sequenciais contra o Express local, com 200 chamadas de aquecimento descartadas e nove rodadas alternadas de cada biblioteca, num MacBook com Apple M4 Pro, Node 24.16.0:
const comFetch = async () => (await fetch(URL)).json();
const comAxios = async () => (await api.get('/pacientes/17')).data;
async function rodada(chamada) {
const t = performance.now();
for (let i = 0; i < N; i++) await chamada();
return performance.now() - t;
}O axios gastou 0,03 ms a mais por chamada — ele monta o envelope de resposta,
roda os interceptores e usa o módulo http do Node, enquanto o fetch vai
direto pelo undici. Trinta microssegundos. Qualquer requisição que saia da sua
máquina custa de 20 a 200 milissegundos, ou seja, mil vezes mais. Escolher
cliente HTTP por desempenho é otimizar o item errado.
A tabela de decisão: quando cada um
Depois de rodar tudo isso, a recomendação da casa é simples e assumida:
| situação | escolha | por quê |
|---|---|---|
| back-end Node chamando uma ou duas APIs | fetch |
já está lá; zero dependência para auditar e atualizar |
| back-end com muitas integrações e política de erro comum | axios | interceptor de resposta centraliza o tratamento |
| front-end com poucas chamadas | fetch |
18 KB de gzip a menos no bundle |
| front-end com login, refresh de token e upload | axios | interceptor no 401 e onUploadProgress |
| projeto com React e cache de servidor | fetch |
quem resolve estado é o TanStack Query, não o cliente |
| script rápido, CLI, cron, função serverless | fetch |
sem node_modules, o cold start agradece |
| projeto que precisa rodar em Node 16 ou mais antigo | axios | ali o fetch global ainda não existe |
E o critério que resume a tabela: comece com fetch e um wrapper de trinta
linhas. Quando o wrapper começar a crescer, quando você precisar de retry com
política, refresh de token e barra de progresso ao mesmo tempo, troque por axios
sem drama — as chamadas mudam de api('/rota') para api.get('/rota'), e é só
isso. O caminho contrário, tirar axios de um projeto grande, é bem mais caro.
O que não é critério: velocidade (0,03 ms), popularidade e “é o que o tutorial usava”.
O que vem depois
Se você chegou aqui pelo lado de quem consome, o passo natural é entender o outro lado do fio: métodos HTTP e status code explica por que o 404 e o 503 deste artigo significam coisas diferentes, e o guia de Node mostra a ordem de estudo até a sua API estar no ar com deploy, healthcheck e variáveis de ambiente.
Se o consumo vai acontecer no navegador, vale revisar
a Fetch API no JavaScript do dia a dia e ver como
isso vira estado de tela em consumir API no React.
Para praticar os mesmos casos sem uma biblioteca por cima, siga para
a Fetch API no JavaScript: nem fetch nem axios
validam, sozinhos, o formato que a clínica devolve.
Para decidir no seu contexto, faça uma comparação pequena: chame uma rota que
responde 404 uma vez com fetch e outra com axios, ambas dentro de
try/catch. Confirme que o fetch exige testar response.ok, enquanto axios
entra no catch. Depois acrescente timeout às duas chamadas. Se um dos exemplos
ficar muito maior que o outro, você encontrou o custo de manutenção que deve
pesar mais do que preferência pessoal.
Perguntas frequentes
O axios ainda é necessário em 2026?
Por que o fetch não dá erro quando a API responde 404?
Preciso instalar node-fetch no Node?
Qual é mais rápido, axios ou fetch?
Dá para fazer barra de progresso de upload com fetch?
axios ou fetch dentro do React?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- MDN — Using the Fetch API — developer.mozilla.org
- Node.js — Globals: fetch — nodejs.org
- Axios — Interceptors — axios-http.com
- MDN — AbortSignal.timeout() — developer.mozilla.org


