Introdução a fetch()

fetch() permite fazer solicitações de rede semelhantes a XMLHttpRequest (XHR). A principal diferença é que a API Fetch usa promessas, que têm uma API mais simples para ajudar a evitar os callbacks complicados na API XMLHttpRequest.

Compatibilidade com navegadores

  • Chrome: 42.
  • Borda: 14.
  • Firefox: 39.
  • Safari: 10.1.

Origem

Se você nunca usou promessas antes, confira Introdução às promessas do JavaScript.

Confira um exemplo implementado com um XMLHttpRequest e depois com fetch. Queremos solicitar um URL, receber uma resposta e analisar como JSON.

XMLHttpRequest

Um XMLHttpRequest precisa de dois listeners para processar os casos de sucesso e de erro, além de uma chamada para open() e send(). Exemplo das documentações do MDN.

function reqListener () {
  const data = JSON.parse(this.responseText);
  console.log(data);
}

function reqError (err) {
  console.log('Fetch Error :-S', err);
}

const oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.onerror = reqError;
oReq.open('get', './api/some.json', true);
oReq.send();

Buscar

Nossa solicitação de busca tem esta aparência:

fetch('./api/some.json')
  .then(response => {
    if (response.status !== 200) {
      console.log(`Looks like there was a problem. Status Code: ${response.status}`);

      return;
    }

    // Examine the text in the response
    response.json().then(function(data) {
      console.log(data);
    });
  })
  .catch(err => {
    console.log('Fetch Error :-S', err);
  });

A solicitação fetch() precisa de apenas uma chamada para fazer o mesmo trabalho do exemplo de XHR. Para processar a resposta, primeiro verificamos se o status dela é 200 e, em seguida, analisamos a resposta como JSON. A resposta a uma solicitação fetch() é um objeto Stream, o que significa que, depois de chamarmos o método json(), uma promessa é retornada. O stream ocorre de forma assíncrona.

Metadados de resposta

O exemplo anterior mostrou o status do objeto Response e como analisar a resposta como JSON. Confira como processar outros metadados que talvez você queira acessar, como cabeçalhos:

fetch('users.json').then(response => {
  console.log(response.headers.get('Content-Type'));
  console.log(response.headers.get('Date'));

  console.log(response.status);
  console.log(response.statusText);
  console.log(response.type);
  console.log(response.url);
});

Tipos de resposta

Quando fizermos uma solicitação de busca, a resposta receberá um response.type de "basic", "cors" ou "opaque". Esses types mostram de onde veio o recurso, e é possível usá-los para determinar como tratar o objeto de resposta.

Quando o navegador solicita um recurso na mesma origem, a resposta tem um tipo basic com restrições sobre o que pode ser visualizado.

Se uma solicitação for feita para um recurso em outra origem e essa origem retornar cabeçalhos CORS, o tipo será cors. As respostas cors são semelhantes às respostas basic, mas restringem os cabeçalhos que podem ser visualizados a Cache-Control, Content-Language, Content-Type, Expires, Last-Modified e Pragma.

As respostas opaque vêm de uma origem diferente que não retorna cabeçalhos CORS. Com uma resposta opaca, não será possível ler os dados retornados nem visualizar o status da solicitação, o que significa que não é possível verificar se a solicitação foi bem-sucedida.

É possível definir um modo para uma solicitação de busca para que apenas determinados tipos de solicitação sejam resolvidos. Os modos que podem ser definidos são:

  • O same-origin só é bem-sucedido para solicitações de recursos na mesma origem e rejeita todas as outras.
  • cors permite solicitações de recursos da mesma origem e outras que retornem os cabeçalhos de CORs apropriados.
  • O cors-with-forced-preflight executa uma verificação pré-voo antes de fazer qualquer solicitação.
  • O no-cors tem como objetivo fazer solicitações para outras origens que não têm cabeçalhos CORS e resultar em uma resposta opaque, mas, conforme declarado, isso não é possível no escopo global da janela no momento.

Para definir o modo, adicione um objeto de opções como o segundo parâmetro na solicitação fetch e defina o modo nesse objeto:

fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
  .then(response => response.text())
  .then(text => {
    console.log('Request successful', text);
  })
  .catch(error => {
    log('Request failed', error)
  });

Encadeamento de promessas

Um dos melhores recursos das promessas é a capacidade de encadeá-las. Para fetch(), isso permite compartilhar a lógica entre as solicitações de busca.

Se você estiver trabalhando com uma API JSON, será necessário verificar o status e analisar o JSON para cada resposta. Simplifique seu código definindo o status e a análise de JSON em funções separadas que retornam promessas e use a solicitação de busca para lidar apenas com os dados finais e o caso de erro.

function status (response) {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  } else {
    return Promise.reject(new Error(response.statusText))
  }
}

function json (response) {
  return response.json()
}

fetch('users.json')
  .then(status)
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  }).catch(error => {
    console.log('Request failed', error);
  });

Este exemplo define uma função status que verifica a response.status e retorna uma promessa resolvida como Promise.resolve() ou uma promessa rejeitada como Promise.reject(). Esse é o primeiro método chamado na cadeia fetch().

Se a promessa for resolvida, o script vai chamar o método json(), que retorna uma segunda promessa da chamada response.json() e cria um objeto que contém o JSON analisado. Se a análise falhar, a promessa será rejeitada e a instrução "catch" será executada.

Essa estrutura permite compartilhar a lógica em todas as suas solicitações de busca, facilitando a manutenção, a leitura e o teste do código.

Solicitação POST

Às vezes, um app da Web precisa chamar uma API com um método POST e incluir alguns parâmetros no corpo da solicitação. Para fazer isso, defina os parâmetros method e body nas opções fetch():

fetch(url, {
    method: 'post',
    headers: {
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: 'foo=bar&lorem=ipsum'
  })
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  })
  .catch(error => {
    console.log('Request failed', error);
  });

Enviar credenciais com uma solicitação de busca

Para fazer uma solicitação de busca com credenciais, como cookies, defina o valor de credentials da solicitação como "include":

fetch(url, {
  credentials: 'include'
})