{"id":254018,"date":"2017-05-02T05:54:46","date_gmt":"2017-05-02T12:54:46","guid":{"rendered":"http:\/\/css-tricks.com\/?p=254018"},"modified":"2020-11-12T13:05:29","modified_gmt":"2020-11-12T21:05:29","slug":"using-fetch","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/using-fetch\/","title":{"rendered":"Using Fetch"},"content":{"rendered":"\n

Whenever we send or retrieve information with JavaScript, we initiate a thing known as an Ajax call. Ajax is a technique to send and retrieve information behind the scenes without needing to refresh the page. It allows browsers to send and retrieve information, then do things with what it gets back, like add or change HTML on the page.<\/p>\n\n\n\n\n\n\n\n

Let’s take a look at the history of that and then bring ourselves up-to-date.<\/p>\n\n\n\n

\n
\n \n Just looking for the basic fetch snippet? Here you go. <\/summary>\n \n\n
fetch(URL)\n  .then(response => response.json())\n  .then(data => {\n    console.log(data)\n  });<\/code><\/pre>\n\n\n<\/details>\n\n\n

<\/p>\n<\/div><\/div>\n\n\n\n

Another note here, we’re going to be using ES6 syntax for all the demos in this article.<\/p>\n\n\n\n

A few years ago, the easiest way to initiate an Ajax call was through the use of jQuery’s ajax<\/code> method:<\/p>\n\n\n\n

$.ajax('some-url', {\n  success: (data) => { \/* do something with the data *\/ },\n  error: (err) => { \/* do something when an error happens *\/}\n});<\/code><\/pre>\n\n\n\n

We could do Ajax without jQuery, but we had to write an XMLHttpRequest<\/code>, which is pretty complicated<\/a>.<\/p>\n\n\n\n

Thankfully, browsers nowadays have improved so much that they support the Fetch API, which is a modern way to Ajax without helper libraries like jQuery or Axios. In this article, I’ll show you how to use Fetch to handle both success and errors.<\/p>\n\n\n

Support for Fetch<\/h3>\n\n\n

Let’s get support out of the way first.<\/p>\n\n\n

This browser support data is from Caniuse<\/a>, which has more detail. A number indicates that browser supports the feature at that version and up.<\/p><\/div>

Desktop<\/h4>
Chrome<\/span><\/th>Firefox<\/span><\/th>IE<\/span><\/th>Edge<\/span><\/th>Safari<\/span><\/th><\/tr><\/thead>
42<\/span><\/td>39<\/span><\/td>No<\/span><\/td>14<\/span><\/td>10.1<\/span><\/td><\/tr><\/table><\/div>

Mobile \/ Tablet<\/h4>
Android Chrome<\/span><\/th>Android Firefox<\/span><\/th>Android<\/span><\/th>iOS Safari<\/span><\/th><\/tr><\/thead>
123<\/span><\/td>124<\/span><\/td>123<\/span><\/td>10.3<\/span><\/td><\/tr><\/table><\/div><\/div>\n\n\n\n

Support for Fetch is pretty good! All major browsers (with the exception of Opera Mini and old IE) support it natively, which means you can safely use it in your projects. If you need support anywhere it isn’t natively supported, you can always depend on this handy polyfill<\/a>.<\/p>\n\n\n

Getting data with Fetch<\/h3>\n\n\n

Getting data with Fetch is easy. You just need to provide Fetch with the resource you’re trying to fetch (so meta!).<\/p>\n\n\n\n

Let’s say we’re trying to get a list of Chris’ repositories on Github. According to Github’s API<\/a>, we need to make a get<\/code> request for api.github.com\/users\/chriscoyier\/repos<\/code>.<\/p>\n\n\n\n

This would be the fetch request:<\/p>\n\n\n\n

fetch('https:\/\/api.github.com\/users\/chriscoyier\/repos');<\/code><\/pre>\n\n\n\n

So simple! What’s next?<\/p>\n\n\n\n

Fetch returns a Promise<\/a>, which is a way to handle asynchronous operations without the need for a callback.<\/p>\n\n\n\n

To do something after the resource is fetched, you write it in a .then<\/code> call:<\/p>\n\n\n\n

fetch('https:\/\/api.github.com\/users\/chriscoyier\/repos')\n  .then(response => {\/* do something *\/})<\/code><\/pre>\n\n\n\n

If this is your first encounter with Fetch, you’ll likely be surprised by the response<\/code> Fetch returns. If you console.log<\/code> the response, you’ll get the following information:<\/p>\n\n\n\n

{\n  body: ReadableStream\n  bodyUsed: false\n  headers: Headers\n  ok : true\n  redirected : false\n  status : 200\n  statusText : \"OK\"\n  type : \"cors\"\n  url : \"http:\/\/some-website.com\/some-url\"\n  __proto__ : Response\n}<\/code><\/pre>\n\n\n\n

Here, you can see that Fetch returns a response that tells you the status of the request. We can see that the request is successful (ok<\/code> is true and status<\/code> is 200), but a list of Chris’ repos isn’t present anywhere!<\/p>\n\n\n\n

Turns out, what we requested from Github is hidden in body<\/code> as a readable stream. We need to call an appropriate method<\/a> to convert this readable stream into data we can consume.<\/p>\n\n\n\n

Since we’re working with GitHub, we know the response is JSON. We can call response.json<\/code> to convert the data.<\/p>\n\n\n\n

There are other methods to deal with different types of response. If you’re requesting an XML file, then you should call response.text<\/code>. If you’re requesting an image, you call response.blob<\/code>.<\/p>\n\n\n\n

All these conversion methods (response.json<\/code> et all) returns another Promise, so we can get the data we wanted with yet another .then<\/code> call.<\/p>\n\n\n\n

fetch('https:\/\/api.github.com\/users\/chriscoyier\/repos')\n  .then(response => response.json())\n  .then(data => {\n    \/\/ Here's a list of repos!\n    console.log(data)\n  });<\/code><\/pre>\n\n\n\n

Phew! That’s all you need to do to get data with Fetch! Short and simple, isn’t it? :)<\/p>\n\n\n\n

Next, let’s take a look at sending some data with Fetch.<\/p>\n\n\n

Sending data with Fetch<\/h3>\n\n\n

Sending data with Fetch is pretty simple as well. You just need to configure your fetch request with three options.<\/p>\n\n\n\n

fetch('some-url', options);<\/code><\/pre>\n\n\n\n

The first option<\/strong> you need to set is your request method<\/em> to post<\/code>, put<\/code> or del<\/code>. Fetch automatically sets the method<\/code> to get<\/code> if you leave it out, which is why getting a resource takes lesser steps.<\/p>\n\n\n\n

The second option<\/strong> is to set your headers<\/em>. Since we’re primarily sending JSON data in this day and age, we need to set Content-Type<\/code> to be application\/json<\/code>.<\/p>\n\n\n\n

The third option<\/strong> is to set a body that contains JSON content. Since JSON content is required, you often need to call JSON.stringify<\/code> when you set the body<\/code>.<\/p>\n\n\n\n

In practice, a post<\/code> request with these three options looks like:<\/p>\n\n\n\n

let content = {some: 'content'};\n\n\/\/ The actual fetch request\nfetch('some-url', {\n  method: 'post',\n  headers: {\n    'Content-Type': 'application\/json'\n  },\n  body: JSON.stringify(content)\n})\n\/\/ .then()...<\/code><\/pre>\n\n\n\n

For the sharp-eyed, you’ll notice there’s some boilerplate code for every post<\/code>, put<\/code> or del<\/code> request. Ideally, we can reuse our headers and call JSON.stringify<\/code> on the content before sending since we already know we’re sending JSON data.<\/p>\n\n\n\n

But even with the boilerplate code, Fetch is still pretty nice for sending any request.<\/p>\n\n\n\n

Handling errors with Fetch, however, isn’t as straightforward as handling success messages. You’ll see why in a moment.<\/p>\n\n\n

Handling errors with Fetch<\/h3>\n\n\n

Although we always hope for Ajax requests to be successful, they can fail. There are many reasons why requests may fail, including but not limited to the following:<\/p>\n\n\n\n

  1. You tried to fetch a non-existent resource.<\/li>
  2. You’re unauthorized to fetch the resource.<\/li>
  3. You entered some arguments wrongly<\/li>
  4. The server throws an error.<\/li>
  5. The server timed out.<\/li>
  6. The server crashed.<\/li>
  7. The API changed.<\/li>
  8. …<\/li><\/ol>\n\n\n\n

    Things aren’t going to be pretty if your request fails. Just imagine a scenario you tried to buy something online. An error occured, but it remains unhandled by the people who coded the website. As a result, after clicking buy, nothing moves. The page just hangs there… You have no idea if anything happened. Did your card go through? 😱.<\/p>\n\n\n\n

    Now, let’s try to fetch a non-existent error and learn how to handle errors with Fetch. For this example, let’s say we misspelled chriscoyier<\/code> as chrissycoyier<\/code><\/p>\n\n\n\n

    \/\/ Fetching chrissycoyier's repos instead of chriscoyier's repos\nfetch('https:\/\/api.github.com\/users\/chrissycoyier\/repos')<\/code><\/pre>\n\n\n\n

    We already know we should get an error since there’s no chrissycoyier<\/code> on Github. To handle errors in promises, we use a catch<\/code> call.<\/p>\n\n\n\n

    Given what we know now, you’ll probably come up with this code:<\/p>\n\n\n\n

    fetch('https:\/\/api.github.com\/users\/chrissycoyier\/repos')\n  .then(response => response.json())\n  .then(data => console.log('data is', data))\n  .catch(error => console.log('error is', error));<\/code><\/pre>\n\n\n\n

    Fire your fetch request. This is what you’ll get:<\/p>\n\n\n\n

    \"\"
    Fetch failed, but the code that gets executed is the second `.then` instead of `.catch`<\/figcaption><\/figure>\n\n\n\n

    Why did our second .then<\/code> call execute? Aren’t promises supposed to handle errors with .catch<\/code>? Horrible! 😱😱😱<\/p>\n\n\n\n

    If you console.log<\/code> the response now, you’ll see slightly different values:<\/p>\n\n\n\n

    {\n  body: ReadableStream\n  bodyUsed: true\n  headers: Headers\n  ok: false \/\/ Response is not ok\n  redirected: false\n  status: 404 \/\/ HTTP status is 404.\n  statusText: \"Not Found\" \/\/ Request not found\n  type: \"cors\"\n  url: \"https:\/\/api.github.com\/users\/chrissycoyier\/repos\"\n}<\/code><\/pre>\n\n\n\n

    Most of the response remain the same, except ok<\/code>, status<\/code> and statusText<\/code>. As expected, we didn’t find chrissycoyier on Github.<\/p>\n\n\n\n

    This response tells us Fetch doesn’t care whether your AJAX request succeeded. It only cares about sending a request and receiving a response from the server, which means we need to throw an error if the request failed.<\/p>\n\n\n\n

    Hence, the initial then<\/code> call needs to be rewritten such that it only calls response.json<\/code> if the request succeeded. The easiest way to do so to check if the response<\/code> is ok<\/code>.<\/p>\n\n\n\n

    fetch('some-url')\n  .then(response => {\n    if (response.ok) {\n      return response.json()\n    } else {\n      \/\/ Find some way to get to execute .catch()\n    }\n  });<\/code><\/pre>\n\n\n\n

    Once we know the request is unsuccessful, we can either throw<\/code> an Error or reject<\/code> a Promise to activate the catch<\/code> call.<\/p>\n\n\n\n

    \/\/ throwing an Error\nelse {\n  throw new Error('something went wrong!')\n}\n\n\/\/ rejecting a Promise\nelse {\n  return Promise.reject('something went wrong!')\n}<\/code><\/pre>\n\n\n\n

    Choose either one, because they both activate the .catch<\/code> call.<\/p>\n\n\n\n

    Here, I choose to use Promise.reject<\/code> because it’s easier to implement. Errors are cool too, but they’re harder to implement, and the only benefit of an Error is a stack trace, which would be non-existent in a Fetch request anyway.<\/p>\n\n\n\n

    So, the code looks like this so far:<\/p>\n\n\n\n

    fetch('https:\/\/api.github.com\/users\/chrissycoyier\/repos')\n  .then(response => {\n    if (response.ok) {\n      return response.json()\n    } else {\n      return Promise.reject('something went wrong!')\n    }\n  })\n  .then(data => console.log('data is', data))\n  .catch(error => console.log('error is', error));<\/code><\/pre>\n\n\n\n
    \"\"
    Failed request, but error gets passed into catch correctly<\/figcaption><\/figure>\n\n\n\n

    This is great. We’re getting somewhere since we now have a way to handle errors.<\/p>\n\n\n\n

    But rejecting the promise (or throwing an Error) with a generic message isn’t good enough. We won’t be able to know what went wrong. I’m pretty sure you don’t want to be on the receiving end for an error like this…<\/p>\n\n\n\n

    \"\"
    Yeah… I get it that something went wrong… but what exactly? 🙁<\/figcaption><\/figure>\n\n\n\n

    What went wrong? Did the server time out? Was my connection cut? There’s no way for me to know! What we need is a way to tell what’s wrong with the request so we can handle it appropriately.<\/p>\n\n\n\n

    Let’s take a look at the response again and see what we can do:<\/p>\n\n\n\n

    {\n  body: ReadableStream\n  bodyUsed: true\n  headers: Headers\n  ok: false \/\/ Response is not ok\n  redirected: false\n  status: 404 \/\/ HTTP status is 404.\n  statusText: \"Not Found\" \/\/ Request not found\n  type: \"cors\"\n  url: \"https:\/\/api.github.com\/users\/chrissycoyier\/repos\"\n}<\/code><\/pre>\n\n\n\n

    Okay great. In this case, we know the resource is non-existent. We can return a 404<\/code> status or Not Found<\/code> status text and we’ll know what to do with it.<\/p>\n\n\n\n

    To get status<\/code> and statusText<\/code> into the .catch<\/code> call, we can reject a JavaScript object:<\/p>\n\n\n\n

    fetch('some-url')\n  .then(response => {\n    if (response.ok) {\n      return response.json()\n    } else {\n      return Promise.reject({\n        status: response.status,\n        statusText: response.statusText\n      })\n    }\n  })\n  .catch(error => {\n    if (error.status === 404) {\n      \/\/ do something about 404\n    }\n  })<\/code><\/pre>\n\n\n\n

    Now we’re getting somewhere again! Yay! 😄.<\/p>\n\n\n\n

    Let’s make this better! 😏.<\/p>\n\n\n\n

    The above error handling method is good enough for certain HTTP statuses which doesn’t require further explanation, like:<\/p>\n\n\n\n