Menu

Fetch API em JavaScript: requisições, JSON e erros

Aprenda a usar a Fetch API do JavaScript na prática: requisições GET e POST, parse de JSON, tratamento de erros e como cancelar requisições com AbortController.

Esta página tem editores executáveis — edite, execute e veja a saída na hora.

Fetch: um cliente HTTP baseado em Promises

A fetch já vem embutida nos navegadores e nas versões modernas do Node. Você passa uma URL, e ela devolve uma Promise que resolve para um objeto Response. No fundo, é essa a API inteira:

Dois .then em sequência porque existem duas etapas assíncronas: primeiro chegam os cabeçalhos da resposta (é isso que a primeira promise entrega), e só depois o corpo é lido e convertido (response.json() também é uma promise). O corpo só é baixado quando você pede.

O mesmo fluxo com async/await fica parecendo código comum, lido de cima para baixo:

Dois await, dois pontos de suspensão. O mesmo trabalho, só que bem mais fácil de ler de cima pra baixo.

O objeto Response

O que a fetch devolve não é o corpo da resposta — é um objeto Response, que traz os metadados e os métodos para você ler esse corpo em diferentes formatos:

Você pode ler o corpo da resposta usando .json(), .text(), .blob(), .arrayBuffer() ou .formData(). Cada um desses métodos retorna uma promise. E tem um detalhe importante: o corpo só pode ser lido uma vez — se você chamar .json() duas vezes na mesma resposta, a segunda chamada vai estourar um erro.

A maior pegadinha: erros HTTP não rejeitam a promise

Essa aqui confunde praticamente todo mundo que está começando com o fetch. Uma resposta 404 ou 500 não é uma rejeição. A promise resolve normalmente, só que com response.ok === false. O fetch só rejeita quando a requisição em si não conseguiu completar — falha de DNS, sem conexão de rede ou bloqueio por CORS.

Na prática, isso significa que um fetch ingênuo vai aceitar numa boa uma página de erro e só vai quebrar depois, na hora do .json():

A solução é verificar response.ok manualmente e lançar um erro quando o servidor retornar um status de erro:

Acostume-se a escrever esse bloco if (!response.ok). Ele merece um lugar em todo wrapper de fetch que você criar.

Fazendo uma requisição POST com fetch

GET é o método padrão. Para qualquer outro verbo HTTP, basta passar um segundo argumento — um objeto de opções:

Três coisas que vale a pena destacar:

  • method tem como padrão "GET". Defina explicitamente quando for POST, PUT, DELETE ou PATCH.
  • body aceita uma string (ou FormData, Blob, etc.) — o fetch não serializa objetos automaticamente para você. O JSON.stringify(...) é responsabilidade sua.
  • O header Content-Type diz ao servidor como interpretar o corpo da requisição. Se esquecer, a maioria dos servidores vai tratar o body como texto puro.

Headers, query strings e outras opções do fetch

Os headers podem ser um objeto comum (ou uma instância de Headers). Já a query string você monta na mão, normalmente com URLSearchParams:

URLSearchParams cuida da codificação pra você — espaços, &, unicode — assim você não acaba com URLs quebradas quando a entrada tem caracteres que precisam ser escapados.

Outras opções que você vai ver em código real: credentials: "include" pra enviar cookies em requisições cross-origin, cache: "no-store" pra ignorar o cache HTTP, e mode: "cors" (que normalmente é o padrão) pra controlar o comportamento de CORS.

Cancelando uma requisição com AbortController

Às vezes você precisa desistir da requisição — o usuário digitou uma nova busca, ou a chamada está demorando demais. É aí que entra o AbortController:

controller.abort() faz com que a promise do fetch seja rejeitada com uma DOMException cujo name é "AbortError". Já o bloco finally limpa o timeout para que uma requisição bem-sucedida não deixe um timer pendurado por aí.

Esse padrão — fetch com timeout e limpeza — vale a pena ser encapsulado em um helper para reutilizar em todo lugar.

Um wrapper reutilizável

Juntando tudo, dá pra montar um pequeno helper que cuida do boilerplate de uma vez só:

Um único lugar pra ajustar headers, um único lugar pra tratar erros, um único lugar pra lidar com respostas vazias. Todo app minimamente sério acaba chegando em algo parecido com isso.

A seguir: tratamento de erros em código assíncrono

O fetch é um dos pontos onde erros assíncronos mais aparecem, e a verificação do response.ok é só uma das peças do quebra-cabeça. A próxima página fala sobre tratamento de erros em promises e async/await — pra onde os erros vão, como capturá-los e as armadilhas que deixam eles passarem despercebidos.

Perguntas frequentes

Como usar o fetch no JavaScript?

Basta chamar fetch(url) passando a URL desejada. Ele retorna uma Promise que resolve em um objeto Response. Para ler o corpo como JSON, chame response.json() (que também é uma promise). Com async/await fica assim: const res = await fetch(url); const data = await res.json();.

Como fazer uma requisição POST com fetch?

Passe um segundo argumento no fetch com method: 'POST', um objeto headers (normalmente 'Content-Type': 'application/json') e o body. Lembre de converter objetos em string com JSON.stringify(...) — o fetch não serializa o corpo automaticamente pra você.

Por que o fetch não dispara erro em status 404 ou 500?

O fetch só rejeita a promise em falhas de rede — erros de DNS, sem conexão, bloqueio de CORS. Para ele, uma resposta HTTP com status de erro ainda é uma resposta válida. Cabe a você checar response.ok (true entre 200 e 299) ou response.status e lançar um erro manualmente quando o servidor retornar algo inesperado.

Dá para cancelar uma requisição fetch?

Dá sim, usando o AbortController. Você cria uma instância, passa o signal dela no objeto de opções do fetch e chama controller.abort() quando quiser cancelar. A promise do fetch vai rejeitar com um AbortError, que você trata normalmente no catch.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR