JACOB SILVER

web developer | musician | venue booker

Fetch

fetch() is a javascript function which allows you to make network requests. It uses promises, which allow for asynchronous requests.

A basic fetch request looks like this:

fetch('./api/party.json')
.then(response => { // do something here }

The response that it returns is actually just the status of the request. We need to convert the body to JSON before we can do anything with the response. For that, we use resonse.json to convert. For example, to get a list of all my repos in my GitHub repository, it would be like this:

fetch('https://api.github.com/users/jacobsilver2/repos')
    .then(response => response.json())
        .then(data => {
            console.log(data)
})

Easy! Now, if you want to post data, it’s not a whole lot different either. You just need to make sure you add a few other parameters (options). It’s like this:

let myData = { data: 'data' }

fetch('./api/party.json', {
    method: 'post',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(myData)
})

Awesome! And it’s not a whole lot different for patch, put, or delete requests either.

It’s also a good idea to have some sort of error-handling as well, because there are many reasons why a fetch request would fail, like, for example, if the internet is down, or if a record doesn’t exist, etc. For that, you need to add a little bit more code at the end. Like this:

fetch('https://api.github.com/users/jacobsilver2/repos')
    .then(response => response.json())
        .then(data => console.log(data))
            .catch(error => console.log(error)

Though…it’s actually not that simple. Mostly because fetch mostly just cares if there is a response returned or not. If there is no response, it will go to the catch portion of the code. But if the response is something like record does not exist, that is technically a response, so it won’t get to the catch. So what I like to do is write a separate function to deal with the response. Like this:

function handleErrors(response){
  if (!response.ok) {
    throw Error(response.statusText);
  }
  return response;
}

There are other bloggers who have written much more elaborate response functions, for example, at css-tricks.com, they use:

function handleResponse (response) {
  let contentType = response.headers.get('content-type')
  if (contentType.includes('application/json')) {
    return handleJSONResponse(response)
  } else if (contentType.includes('text/html')) {
    return handleTextResponse(response)
  } else {
    // Other response types as necessary. I haven't found a need for them yet though.
    throw new Error(Sorry, content-type ${contentType} not supported)
  }
}

function handleJSONResponse (response) {
  return response.json()
    .then(json => {
      if (response.ok) {
        return json
      } else {
        return Promise.reject(Object.assign({}, json, {
          status: response.status,
          statusText: response.statusText
        }))
      }
    })
}
function handleTextResponse (response) {
  return response.text()
    .then(text => {
      if (response.ok) {
        return json
      } else {
        return Promise.reject({
          status: response.status,
          statusText: response.statusText,
          err: text
        })
      }
    })
}

But however you decide to do it, your initial fetch request would have to be slightly refactored to something like:

fetch('./api/party.json')
  .then(handleResponse)
  .then(data => console.log(data))
  .catch(error => console.log(error))

But however you decide to do it, know that it is now MUCH easier than the previous method of using XMLHttpRequest or jQuery!