Friday, 15 June 2018

Common `fetch` errors

The new fetch API is a great improvement on XMLHttpRequest but it does have a very surprising behavior that catches people out a lot. Separately, because it uses promises, a lot of people using it fall into a common promise-related error as well. This post is a quick note about both of those.

Let's say we have an endpoint that returns JSON. This is the common code you see for consuming that:

function showStuff() {
    fetch("/get/stuff")
        .then(response => response.json())
        .then(data => {
            // ...show the stuff from `data`
        });
}

There are two significant errors in the above, can you see them?

Right! They are:

  1. fetch only rejects on network errors; HTTP-level errors (like 404s or 500s) are still resolutions, but the above assumes that resolution = all is good.
  2. Rejections aren't handled at all.

So if the server is overloaded or some such, instead of handling it gracefully, the code will just fail as of the result.json() call since what it gets back is probably an HTML page for a 500 error, not JSON. And then it completely fails to handle that error, resulting in an "Unhandled rejection" error in the console.

I'm going to take these one at a time because they are completely distinct issues.

To fix #1, we need to check for response.ok, which is set for any response where the HTTP status code is 200-299 ("success"):

function showStuff() {
    fetch("/get/stuff")
        .then(response => {
            if (!response.ok) {
                throw new Error("Failed with HTTP code " + response.status);
            }
            return response;
        })
        .then(result => result.json())
        .then(data => {
            // ...show the stuff from `data`
        });
}

There I've turned the resolution with a non-ok status into a rejection by throwing in the then handler. You may choose to do something different, but making it a rejection means all my following logic can just work on the basis that things were okay.

If, like me, you always want to do that, you might give yourself a helper function:

function myFetch(...args {
    return fetch(...args).then(response => {
        if (!response.ok) {
            throw new Error("Failed with HTTP code " + response.status);
        }
        return response;
    });
}

Now I can use myFetch and know that resolutions are for completely successful calls. There are lots of ways one might vary that, but that's the basic idea. Now our example is:

function showStuff() {
    myFetch("/get/stuff")
        .then(result => result.json())
        .then(data => {
            // ...show the stuff from `data`
        });
}

That's #1 sorted. But the second problem remains.

For #2, we need to follow the promise commandment:

Handle errors, or return the promise to something that does.
(I don't know if anyone actually said that. Well, anyone but me.) In our example situation, it appears that showStuff is meant to be fairly standalone, so we'd handle errors inline:

function showStuff() {
    myFetch("/get/stuff")
        .then(result => result.json())
        .then(data => {
            // ...show the stuff from `data`
        })
        .catch(err => {
            // ...do something with the error that occurred
        });
}

But it would also be perfectly valid to make showStuff return the promise. In that case, we probably wouldn't call it showStuff, but perhaps getFormattedStuff on the assumption the caller will either handle the error or use the formatted result:

function showStuff() {
    return myFetch("/get/stuff")
        .then(result => result.json())
        .then(data => {
            // ...format the stuff from `data`
            return formattedStuff;
        });
}

Now it's up to the caller to handle things, both resolution (with formatted stuff) or rejection.

23 comments:

Unknown said...

When an error does occur, is there anything else that should be done besides visually prompting the user that there was a problem and/or logging the error in some type of error monitoring system?

Suwanwannapho said...

Optional add examples
From suwanwannapho(google)

Suwanwannapho said...

Optional add examples

Rahul Singh said...

Great post. It’s a fact that your blog posts are so unique and interesting. I have always admired your site. Thanks for the great tips and work.
filter papers

Rahul Sharma said...
This comment has been removed by the author.
Rahul Sharma said...

Very well written post. You have shared a wonderful article which is very helpful. Thanks for sharing, have a good day. membrane filters

The Luxury Trains said...

I hope your thoughts do get published, because I’ll be really interested in read it.
palace on wheels price

Tyler Worgan said...

I have read your article, it is very informative and helpful for me.I admire the valuable information you offer in your articles. Thanks for posting it.
hypoluxo tree service

Tyler Worgan said...

Hi, This is a nice article you shared great information i have read it thanks for giving such a wonderful Blog for the reader.
dumpster rental lee county

Tyler Worgan said...

Thanks for the wonderful share. Your article has proved your hard work and experience you have got in this field. Brilliant .i love it reading. residential dumpster rental fort myers

gracerolland said...

I think this is one of the most significant information for me. And i’m glad reading your article. home automation pembroke pines

Tyler Worgan said...

I have read your article, it is very informative and helpful for me.I admire the valuable information you offer in your articles. Thanks for posting it..
window security fort lauderdale

jasminegates said...

This is a great article thanks for sharing this informative information. I will visit your blog regularly for some latest post.junk removal lee county

gracerolland said...

You have a good point here!I totally agree with what you have said!! Thanks for sharing your views. hope more people will read this article!!! junk removal services cape coral

Tyler Worgan said...

Hi, This is a nice article you shared great information i have read it thanks for giving such a wonderful blog for the reader. dumpster rental companies fort myers

jasminegates said...

I think this is one of the most significant information for me. And i’m glad reading your article. glass break detection miami gardens

gracerolland said...

You have a good point here!I totally agree with what you have said!! Thanks for sharing your views. hope more people will read this article!!! vacation burglar mode miami beach

Tyler Worgan said...

I think this is one of the most significant information for me. And i’m glad reading your article. house alarm security systems doral

jasminegates said...

I have read your article, it is very informative and helpful for me.I admire the valuable information you offer in your articles. Thanks for posting it..gym cleaning broward county

Daniel Bryan said...

I read the post and I really liked it. You must add something more to your post in order to increase audience. Keep your work up.
junk removal austin

gracerolland said...

I think this is one of the most significant information for me. And i’m glad reading your article. cabinet installation vero beach

Tyler Worgan said...

I have read your article, it is very informative and helpful for me.I admire the valuable information you offer in your articles. Thanks for posting it..home renovation port st lucie

RhainneColes said...

If you want to have the concept of "global errors", you can create an errors reducer, which can listen for addError, removeError, parking lot maintenance san jose ca