Reputation: 19202
So I have some code like this:
async function getData() {
const response = await fetch(/* ... */);
const json = await response.json();
return transform(json);
}
Where transform
can throw some of its own errors.
I'm try to catch for network errors from the fetch
API.
try {
const data = await getData();
// ...
return // ...
} catch (e) {
if (isNetworkError(e)) {
return localStorage.getItem('...');
}
throw e;
}
My question is how do I implement isNetworkError
that works across browsers? Note: that this should only return true if the network is offline.
It seems like both chrome and firefox throws a TypeError
but the messages they have are different on each.
TypeError: "NetworkError when attempting to fetch resource."
TypeError: Failed to fetch
Upvotes: 17
Views: 19223
Reputation: 187
I am late to the party, but I found a better solution to this issue.
If you aim to check if the user is offline, you can do that by using navigator.onLine
on the catch.
.catch((error) => {
if(!navigator.onLine) {
handleOffline()
}
})
Upvotes: 1
Reputation: 1144
I always get errors in catch of promisses.
var cardData = new Map()
async function requestData(index, modality, callBack){
cardData['index'] = index
cardData['modality'] = modality
await fetch("https://localhost:3000/someEndPoint",{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(cardData),
})
.then((response) => response.json())
.then((data) => {
callBack({data: data})
})
.catch((error) => {
callBack({error: error})
})
}
requestData(index, modality, (callBack) =>{
if(data.error == undefined){
//request made successfully
//callBack.data
} else{
//filter errors
//callBack.error
}
})
Upvotes: 0
Reputation: 1219
When using fetch
, you can't differentiate network errors from other errors caused by building an incorrect request, as both are thrown as TypeError
s. (See https://developer.mozilla.org/en-US/docs/Web/API/fetch#exceptions).
This is quite a flaw, as application defects that cause an incorrectly built request may go unnoticed, masked as if they were circumstantial network errors.
Upvotes: 14
Reputation: 63478
I made a package that makes it easy to check whether an error is a network error:
import isNetworkError from 'is-network-error';
async function getUnicorns() {
try {
const response = await fetch('unicorns.json');
return await response.json();
} catch (error) {
if (isNetworkError(error)) {
return localStorage.getItem('…');
}
throw error;
}
}
console.log(await getUnicorns());
If you don't want to depend on a package for this, feel free to copy-paste the contents into your project.
Upvotes: 8
Reputation: 11017
If the first promise rejects, it's a network error. That's the only time it does.
The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything prevented the request from completing.
From Mozilla developer page: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
Edit: As pointed out in the comments and other answers, you should also pay attention to the line "or if anything prevented the request from completing". Which means that the initial Promise of fetch
will reject on network errors in addition to other problems. Like for example, an invalid URL or a CORS error.
If fetch is able to successfully reach the server with your request, it will resolve the first Promise successfully, otherwise the first promise will reject. In the case of CORS, the error occurs before your request is actually sent out (in the OPTIONS request), which is why the error occurs in the first Promise.
Upvotes: 12
Reputation: 99505
I don't have a perfect answer for you. The easiest to solve your exact problem is to implement isNetworkError
as a type guard. Maybe there are common properties on the firefox/chrome error object that allows you to detect specifically these.
The alternative is to catch errors closer to where you do fetch()
and rethrow the error as something you can easily detect.
async function getData() {
let response;
try {
response = await fetch(/* ... */);
} catch (err) {
throw new MyCustomNetworkError(err.message);
}
const json = await response.json();
return transform(json);
}
Upvotes: 3