Reputation: 13286
Is this the only way to use the body.json() and also get the status code?
let status;
return fetch(url)
.then((response => {
status = response.status;
return response.json()
})
.then(response => {
return {
response: response,
status: status
}
});
This doesn't work as it returns a promise in the response field:
.then((response)=> {return {response: response.json(), status: response.status}})
Upvotes: 67
Views: 117758
Reputation: 6768
The .json
method returns a promise, not the parsed value itself. If you want to access both the response and the parsed value in the same callback, you'll need to use nested functions like this:
fetch(url).then(response => {
response.json().then(parsedValue => {
// code that can access both here
})
});
Alternatively, you can use await
inside an asynchronous function to eliminate the need for callbacks.
const response = await fetch(url);
const parsedValue = await response.json();
// code that can access both here
Of course, you'll want to check for errors, either with a .catch(...)
call on a Promise or with a try...catch
block in an async
function. You could make a function that handles JSON and error cases, and then reuse it for all fetches. For example, something like this:
function handle(response) {
if (response.ok) {
return response.json().then(parsedValue => {
// the status was ok and the body could be parsed
return { response, parsedValue };
}).catch(err => {
// the status was ok but the body was empty or not JSON
return { response };
});
} else {
return response.json().catch(err => {
// the status was not ok and the body was not JSON
throw new Error(response.statusText);
}).then(parsedValue => {
// the status was not ok and the body was JSON
throw new Error(parsedValue.error); // assuming our API returns an object with an error property
});
}
}
I don't think it's the best design pattern, but hopefully this clarifies how the fetch API works. Note that the call to fetch
itself can throw an error if something prevents the request from being sent in the first place.
PS: I avoided naming any variable or property json
since that is the name of the text format. Once it's been parsed, it is no longer JSON. You might want to use a more meaningful name than parsedValue
.
Upvotes: 33
Reputation: 943220
The introduction of async
and await
makes it easy to handle data from dependant promises in one place.
Since it doesn't involve the use of callback functions, the variables all exist in the same scope.
const myFunction = async (url) => {
const response = await fetch(url);
const status = response.status;
const data = await response.json();
return { data, status };
};
Upvotes: 1
Reputation: 316
Using two 'then's seem unnecessary to me.
async/await could get the job done pretty easily.
fetch('http://test.com/getData')
.then( async (response) => {
// get json response here
let data = await response.json();
if(data.status === 200){
// Process data here
}else{
// Rest of status codes (400,500,303), can be handled here appropriately
}
})
.catch((err) => {
console.log(err);
})
Upvotes: 15
Reputation: 68645
Your status is not visible in the second then
. You can just get the two properties in the single then
.
json()
returns a new Promise to you, so you need to create your object inside the then
of the result of that function. If you return a Promise from a function, it will be fulfilled and will return the result of the fulfillment - in our case the object.
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then(r => r.json().then(data => ({status: r.status, body: data})))
.then(obj => console.log(obj));
Upvotes: 134
Reputation: 5988
Did you try this?
return fetch(url)
.then((r)=> {return {response: r.json(), status: r.status}})
Upvotes: 0