Fetch API e o JavaScript

Trabalha com desenvolvimento web há mais de 12 anos, atuando com projetos open source, palestrante, instrutor, e sócio fundador da Nasc, além de ser curador e idealizador da ​BrazilJS. Também um GDE.

A API Fetch é mais uma API que veio para nos ajudar e facilitar certos trabalhos repetitivos.

No lugar de usarmos os antigos objetos XMLHTTPRequest, podemos usar a API fetch para lidar com requisições HTTP.

Promise

Uma coisa muito legal é que a Fetch API segue o padrão de Promise.

const URL_TO_FETCH = 'https://braziljs.org/api/list/events';
fetch(URL_TO_FETCH, {
  method: 'get' // opcional 
})
.then(function(response) { 
  // use a resposta 
})
.catch(function(err) { 
  console.error(err); 
});

Simples assim… ou nem tanto!

Como tratar a resposta do fetch

A resposta retornada pelo fetch trata-se de um objeto Response. Este objeto tem diversas métodos extremamente úteis, dentre eles, o método text(), o qual podemos usar para recuperar a informação retornada pela requisição, no formato texto. Este método nos retornará uma Promise que, quando resolvida, nos entregará o conteúdo da requisição:

const URL_TO_FETCH = 'https://braziljs.org/api/list/events';
fetch(URL_TO_FETCH, { 
  method: 'get' // opcional 
})
.then(function(response) { 
  response.text()
  .then(function(result){ 
     console.log(result); 
   }) 
})
.catch(function(err) { console.error(err); });

Fetching JSON

O objeto Response também tem o método json, que também retorna uma promise, para facilitar caso esteja acessado uma API JSON.

fetch(URL_TO_FETCH)
  .then(function(response){
    response.json().then(function(data){
      console.log(data);
    });
  })
  .catch(function(err){
    console.error('Failed retrieving information', err);
  });

Usando syntaxe ES6

Podemos nos utilizar das atualizações da linguagem, como uso de arrow functions, além de otimizar a forma como lidamos com promises, evitando encadeamento.

fetch(URL_TO_FETCH)
  .then(response => response.json()) // retorna uma promise
  .then(result => {
    console.log(result);
  })
  .catch(err => {
  // trata se alguma das promises falhar
  console.error('Failed retrieving information', err);
});

[UPDATE] Este trecho foi acrescentado após sugestão nos comentários.

Fetch de outros tipos/objetos

Mas também podemos recuperar o resultado da requisição lendo seus bytes, para por exemplo, carregar uma imagem, pdf, ou outro tipo de formato/blob. Para conseguirmos isto, precisamos acessar um reader da resposta. Para isto, usamos o método getReader vindo no body da resposta.

let reader = response.body.getReader();

O interessante é que um objeto Response só pode ser manuseado por um único reader. Sendo assim, após manusea-lo, não é possível pegar outro reader.

O reader tem o método read, que retorna uma promise:

reader.read().then(function(data){
  // use o data
});

Agora sim, podemos usar o conteúdo! Ou quase isto!

Esta Promise é resolvida com um objeto que tem o conteúdo retornado pela requisição em sua propriedade value. Mas ela vem otimizada em Uint8Array. Se quiser transformar este objeto em String, podemos usar o fromCharCode do objeto String.

console.log(
  String.fromCharCode.apply(null, data.value)
);

Caso tenha alguma dificuldade com o método apply do objeto Function, leia meu artigo sobre call, bind e apply.

Em resumo, caso queira usar a resposta utilizando readers, use:

fetch(URL_TO_FETCH)
  .then(function(response){
    response.body
      .getReader()
      .read()
      .then(function(data){
        // transformando em string, para
        // visualizarmos melhor
        var fetched = String.fromCharCode.apply(null, data.value);
        console.log(fetched);
    });
  })
  .catch(function(err){
    console.error('Failed retrieving information', err);
  });

Fetch Options

Em nosso primeiro exemplo, passamos também um objeto com a propriedade method. Este objeto é opcional, onde method será por padrão “get”.

Mas este objeto nos permite passar alguns outros parâmetros.

fetch(URL_TO_FETCH, {
  method: 'POST',
  mode: 'cors', // pode ser cors ou basic(default)
  redirect: 'follow',
  headers: new Headers({
    'Content-Type': 'text/plain'
  })
}).then(function(response) {
  // tratar a response
});

Podemos criar a requisição utilizando os métodos:

  • GET
  • POST
  • PUT
  • DELETE
  • HEAD

Você deve ter reparado no uso de new Headers.

Headers

Instancias de Header podem ser passadas para o objeto Request (criado implicitamente pelo fetch), a fim de de possibilitar a manipulação das propriedades de header da requisição.

// Podemos instanciar um Header já passando um objeto 
// com as propriedades que queremos setar 
var headers = new Headers({ 'Content-Type': 'text/plain', 'X-Custom-Header': 'valor' });

// mas também podemos adicionar programaticamente 
// depois de já termos instanciado 
headers.append('X-Custom-Header', 'valor');

// além disso podemos checar, pegar ou setar valores 
headers.get('Content-Type'); 

// "text/plain" 
headers.set('Content-Type', 'application/json'); 
headers.has('Content-Type'); // true

// assim como deletar propriedades de Header 
headers.delete('X-Custom-Header');

Fetch por Post

Quando queremos enviar dados pelo método POST, podemos passar estas informações em um objeto na propriedade body da requisição.

Caso queiramos passar os dados de um formulário pela requisição, podemos utilizar o objeto formData:

fetch(URL_TO_POST, {
  method: 'post',
  body: new FormData(document.querySelector('#my-form'))
}).then(function(response){
  // trate a resposta aqui
});

Conclusão

A Fetch API nos facilita muito o trabalho no dia-a-dia, e também formaliza e padroniza a forma como faremos requisições. Uma coisa importante também, é que a Fetch API é a base para outras tecnologias que estão surgindo e que serão abordadas em outros posts, como Cache API e Web Services.

Algumas fontes para leitura:


BrazilJS é uma iniciativa NASC