Requests don’t fail much. If they fail, they fail because of these two reasons:
Connection errors
Servers returning errors
Connection errors
You won’t be able to send requests or receive responses when you’re offline.
Try sending a request without internet connection. You’ll see an error like this (assuming you’ve downloaded zlFetch onto your computer and linked it to your index.html file).
You also won’t be able to receive a response from a request if your internet connection gets cut off midway.
One way to simulate a cut-off in connection is to disconnect your Wifi while sending a request.
// 10.255.255.1 gives you time to disconnect your wifi
zlFetch('http://10.255.255.1')
.then(response => { console.log(response) })
.catch(error => console.error(error))
You’ll get an error message that looks like this:
In both cases, you’ll get TypeError: Failed to fetch as your error message. You won’t be able to get the net:: part in JavaScript.
Handling connection errors
Some apps handle connection errors by telling users they’re offline. Slack does this, for example.
We can copy Slack and tell users they’re offline too.
First, we’ll use navigator.onLine to check whether a user is online. If the user is online, navigator.onLine return true. If the user is offline, navigator.onLine returns false
console.log(navigator.onLine) // true or false, depending on whether you're online or offline
Second, we’ll adjust the error message depending on whether the user is online.
todolist.addEventListener('submit', ev => {
// ...
zlFetch(/*...*/)
.then(/*...*/)
.catch(_ => {
// ...
const errorMessage = navigator.onLine
? 'Cannot add task. Please try again later.'
: `It seems like you're offline. Please go online to use the Todolist.`
// ...
})
})
If the user goes offline while sending a request, they’ll see this message:
If they’re online, they’ll see this message:
A better way to handle connection errors
We don’t have to wait for a user to send a request before we check if they’re offline. We can tell whether they’re online or offline anytime with two events.
online: Fires when user comes online
offline: Fires when user goes offline
We can build a warning message to inform users when the moment they go offline. It’s friendly compared to an error message.
We can build the warning message with the following HTML:
<div class="flash-container">
<div class="flash flash--connection" data-type="warning">
<svg class="flash__icon"> <!-- ... --> </svg>
<span class="flash__message">It seems like you're offline. Please go online to use the Todolist.</span>
<button class="flash__close"> <!-- ... --> </button>
</div>
</div>
We’ll only show the message when the user is offline. To do this, we can add a custom attribute called data-connection-status to the <body> element.
If a user goes offline when they’re using the Todolist, they’ll trigger an offline event. Likewise, if they come online when they’re using the Todolist, they’ll trigger an online event.
We’ll use the events to update the connection status.
If you go offline and refresh the Todolist, you’ll see a list of error messages. You get these errors because the browser can’t fetch libraries when you’re offline. (You can’t send requests or receive responses. Remember?)
You can mitigate these errors by downloading the libraries into your project. (Highly recommended for real projects).
Anyway, the zlFetch is not defined error prevents the rest of the JavaScript file from executing. If you want online/offline code to work, you’ll want to put them at the start of the JavaScript file.
// Put these at the start of your JavaScript file
setConnectionStatus()
window.addEventListener('online', setConnectionStatus)
window.addEventListener('offline', setConnectionStatus)
Server errors
Servers return errors because of three reasons:
The browser can’t reach the server
Something went wrong in the server
Client errors
When the browser can’t reach the server
It’s a connection error when the browser can’t reach the server. You’ll also get an error message that says TypeError: Failed to fetch. This happens even if the server goes offline.
In this case, we don’t know why we can’t reach the server. The best error message we can say is Failed to reach server. It’s more friendly compared to TypeError: Failed to fetch.
todolist.addEventListener('submit', event => {
// ...
zlFetch(/*...*/)
.then(/*...*/)
.catch(error => {
// ...
let errorMessage = ''
const { message } = error.body
if (message = "TypeError: Failed to fetch") {
errorMessage = 'Failed to reach server. Please try again later.'
} else {
errorMessage = 'Cannot add task. Please try again later.'
}
// ...
})
})
I usually to add a Please try again later message just incase something went wrong with the connection temporarily. If the user gets the Please try again later message in two different occasions, they’ll usually contact me so I can fix the problem.
Something went wrong with the server
Developers who write code for servers can make mistakes too. If something goes wrong inside the server (that has nothing to do with your request), the server should return a 500+ Internal Server Error.
We don’t know what error messages might come from the server in this case. It might be something weird like E11000 duplicated key error if the developer did not send a proper error message.
What we can do is use the error message from the server as a default. If we notice a specific error message later, we can replace it with our own (like the TypeError one).
Again, we don’t know what error messages come from the backend. We can only hope the developer created proper error messages for us.
If you get a friendly error message, you can use the error message directly. For example, if you send in a Bad Request, you’ll get a friendly error message that says Task requires a name.
Sometimes, you don’t get friendly messages. For example, if you sent set a wrong username or password for the Authorization header, you’ll get an error message that says Unauthorized.
Unauthorized isn’t friendly. We can choose to change it up to be slightly more friendly.
todolist.addEventListener('submit', event => {
// ...
zlFetch(/*...*/)
.then(/*...*/)
.catch(error => {
// ...
if (message = 'TypeError: Failed to fetch') {
errorMessage = 'Failed to reach server. Please try again later.'
} else if (message = 'Unauthorized') {
errorMessage = 'Invalid username or password. Please check your username or password.'
} else {
errorMessage = error.body.message
}
// ...
})
})