4

I am using fetch api for fetching an URL that might return:

Response : status = 200, json body = {'user': 'abc', 'id': 1}

or

Response : status = 400 , json body = {'reason': 'some reason'}

or

Response : status = 400 , json body = {'reason': 'some other reason'}

I want to make a separate function request() that I use from various parts of my code as follows:

 request('http://api.example.com/').then( // status 200 comes here data => // do something with data.id, data.user ).catch( // status 400, 500 comes here error => // here error.reason will give me further info, i also want to know whether status was 400 or 500 etc ) 

I am unable to do the split between 200 and 400,500 (i have tried by throwing an error). When I throw an error, I am finding it hard to still extract the JSON body (to use for error.reason).

My current code is as follows:

import 'whatwg-fetch'; /** * Requests a URL, returning a promise */ export default function request(url, options={}) { console.log('sending api request, url = ' + url) return fetch(url, options) .then(checkStatus) .then(parseJSON) .then((data) => ({data})) .catch((err) => ({err})); } function checkStatus(response) { if (response.status >= 200 && response.status < 300) { return response; } const error = new Error(response.statusText); error.response = response; throw error; } function parseJSON(response) { return response.json(); // json() is a promise itself } 

I have tried to solve this by doing as follows, by inverting the order of .then() calls, but does not work

export default function request(url, options) { return fetch(url, options) .then(parseJSON) // note that now first calling parseJSON to get not just JSON but also status. .then(checkStatus) // i.e. Inverted order of the two functions from before .then((data) => ({data})) .catch((err) => ({err})); } function checkStatus({data, status}) { if (status >= 200 && status < 300) { return data; } else { // const error = new Error(response.statusText); const error = new Error("Something went wrong"); // error.response = response; error.data = data; throw error; } } function parseJSON(response) { let jsonBody response.json().then(json => { jsonBody = json // this does not help, i thought it will make jsonBody fill up, but seems its in a diff thread }) return { data: jsonBody, status: response.status // my aim is to send a whole dict with status and data to send it to checkStatus, but this does not work } } 
3
  • The error comes is in the second callback in .then -> request('http://api.example.com/').then( function(data){ //success call back, i.e 200 }, function( jqXHR, textStatus, errorThrown) { // error call back. Check the status code here } ); Commented Sep 6, 2016 at 2:05
  • this does not help - the issue with that code is that Promises don't make asynchronous code synchronous (the code assumes they do) Commented Sep 6, 2016 at 2:16
  • duplicate of Cleanest way to handle custom errors with fetch & ES6 promise or fetch: Reject promise with JSON error object? Commented Sep 6, 2016 at 6:05

2 Answers 2

8

response.json() returns an asynchronous result. You are not returning the object at parseJSON from within .then() chained to response.json(). To correct that issue you can return response.json() promise at parseJSON call and return object containing data and status from within .then() chained to response.json()

function parseJSON(response) { return response.json().then(json => { return { data: json, status: response.status } }) } 
Sign up to request clarification or add additional context in comments.

3 Comments

you can actually shorten that functions body to return response.json().then(json => ({ data: json, status: response.status }));
@JaromandaX Yes, did not want to further complicate matters at Answer
looks like the OP is familiar enough with arrow functions to even do var parseJSON = response => response.json().then(json => ({ data: json, status: response.status }));
0

Here's slightly different approach: With a one-liner I create a response-like promise with ok, status and json-as-object (not a promise), then I decide what to do with this object. Generally I reject with response if response.ok is false, otherwise I resolve with only the json-data. Network errors/json-parse-errors are rejected as usual.

fetch(url, options) .then(r => r.json().then(json => ({ok: r.ok, status: r.status, json}))) .then( r => r.ok ? r.json: Promise.reject(r)) 

2 Comments

As far as I know using the spread operator with response doesn't work but results in undefined. So your es6 example is wrong. Can you confirm that?
@webwelten: You're right, and I don't know why I wrote that. I believe it worked for me when I wrote it (I usually test code before I post). However that code (now abondoned) went through a compile step (babel and/or typescript, if I remember correctly) to old-style JS, so perhaps that's the reason. I will remove that line from the answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.