10

I am trying to intelligently handle the success/error responses from our API using fetch & ES6 promises.

Here is how I need to handle response statuses:

204: has no json response, but need to treat as success 406: should redirect to sign in 422: has json for error message < 400 (but not 204): success, will have json >= 400 (but not 422): error, will not have json 

So, I am struggling with how to write this cleanly.

I have some less than stellar code working right now that looks like this:

fetch() .then(response => checkStatus(response)) .then(parseJSON) //will throw for the 204 .then(data => notify('success', someMsg)) .catch(error => checkErrorStatus(error)) .then(parseJSON) .then(data => notify('error', dataForMsg) .catch(error => notify('error', someGenericErrorMsg) 

But it seems pretty weird to use catch twice and I don't know how to deal with that 204 just yet.

Also, just to clarify checkStatus and checkErrorStatus do a similar thing:

export function checkStatus(response) { if (response.status >= 200 && response.status < 300) { return response } else { let error = new Error(response.statusText) error.response = response throw error } } function checkErrorStatus(error) { if(error.response.status === 422) { return error.response } else { let error = new Error(response.statusText) error.response = response throw error } } 

Any suggestions for cleaning this up?

6
  • oh just something simple like this: export function parseJSON(response) { return response.json() } Commented Nov 25, 2015 at 2:31
  • For the 422 case see this question Commented Nov 25, 2015 at 2:35
  • What does "< 400 (but not 422)" mean? Commented Nov 25, 2015 at 2:35
  • Well, a response from the server with a status of 422 will have some JSON that has things like validation error messages that I use to display in an error notification. But all other 400's won't have that json. They will need to be treated differently. Commented Nov 25, 2015 at 2:38
  • I meant that it's supposed to be "< 400: success" and ">= 400 (but not 422): error, no json" Commented Nov 25, 2015 at 2:43

2 Answers 2

22

I think you can write it out pretty easily:

fetch(…).then(response => { if (response.ok) return response[response.status == 204 ? "text" : "json"](); if (response.status == 422) return response.json().then(err => { throw err; }); if (response.status == 406) var error = new AuthentificationError(response.statusText); // or whatever else var error = new Error(response.statusText) error.response = response throw error; }) 
Sign up to request clarification or add additional context in comments.

5 Comments

Yeah, this is nice, you thought of at least two things in there that might not have occurred to me. And now that I look at this, this problem would serve as a good challenge in an interview!
Just a little note, my webpack setup complained about throw err not being in curly brackets, but other than that your solution crushed it.
@jasongonzales: Thanks - now that you mention it, my mental syntax parser sounds the alarm as well :-)
Just FYI, you can't use let in a single-line if block. You'd need to declare let error further up the scope, or use...var :)
@RGraham: Good catch, thanks. I just learned that it's even a syntax error, as let declarations are no statements, they are statementlistitems.
3

Following on from Bergi's solution, you might consider mechanising the handling of responses with a fairly simple ResponseHandler() object;

function ResponseHandler() { this.handlers = []; this.handlers[0] = function() {}; // a "do nothing" default handler } ResponseHandler.prototype.add = function(code, handler) { this.handlers[code] = handler; }; ResponseHandler.prototype.handle = function(response) { var h = this.handlers, s = response.status, series = Math.floor(s / 100) * 100; // 100, 200, 300 etc (h[s] || h[series] || h[0])(response); // sniff down the line for a specific/series/default handler, then execute it. }; 

In use :

// create an instance of ResponseHandler() and add some handlers : var responseHandler = new ResponseHandler(); responseHandler.add(204, function(response) {...}); // specific handler responseHandler.add(422, function(response) {...}); // specific handler responseHandler.add(406, function(response) {...}); // specific handler responseHandler.add(200, function(response) {...}); // 200 series default handler responseHandler.add(400, function(response) {...}); // 400 series default handler responseHandler.add(0, function(response) {...}); // your overall default handler // then : fetch(…).then(response => { responseHandler.handle(response); }); 

You would lose the efficiencies of a hard coded solution such as Bergi's, but potentially benefit from improved manageability and reusability.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.