Modern javascript http clients use promises or somethings similar (for this post, angular Observables are similar enough, so this post also is relevant to angular), and a common issue with this approach is that sometimes we want to show a default message to the user when something went wrong
I am using the fetch standard API in this blog, but the same can be achieve with similar libraries like the browser axios or the default angular http client.
Let imagine we need to access a rest API, so we create a simple class to handle all the logic and interactions with this API, like this:
This example uses the excellent (and free) json placeholder example API to handle a list of todos
.
With this class, we can get the list using:
This will print all the todos, if we want to handler the errors, we can simply add a catch
clause:
This is fairy easy and common in many modern web apps, but the problem is when we want to add a global error handler.
Let’s suppose the server is down, we can check this like this:
This will work, but the problem is that the we don’t want to check in every call if the server is down, and also this will provoke that we will have duplicated code everywhere, now we will see some options.
First attempt, global handler in the APICaller
What if we check for this kind of errors in the APICaller
class?, lets change our handleError
method, like this:
In this case we can remove the catch, and all the exception will be correctly handled in our method, we can try to extend this will every other statusText
message.
But what happens if we want to handle some exceptions in our code, and all the others in the global handler?
Well this will not work, because if we try something like this:
The console will print two messages, first the global one and then the personalized message
, this is because the order of execution is like this:
- First is executed the fetch
- If there is an error the global handler is called
- Finally the custom
catch
is invoked
We can’t change the behaviour of the second step we we are in the third, but, we have some options!
First approach, parameter in the call
We can change the method getTodoList
so it accept and argument, and object with options to our APICaller
, for example, we can do this:
This will work, and if we pass the second parameter, the global message will not apear. The real issue with this approach is that we need to add an extra object to every single call.
Second approach, flag on the response
We can also use the fact that we can add arbitrary props to an exception, for example, we can add a flag to the error when we catch it.
For this approach to work, we need to change the order of the execution, to this:
- First is executed the fetch
- The custom
catch
is invoked - The global handler is executed
We can implement this approach fairy easy with the help the setTimeout
method, like this:
This will work, and we have total control about the global error message, the issue it’s that we need to change every single catch to touch that flag.
Another advantage of this method is that we can do some code completely unrelated to the exception in the catch (cleanup for example) and if we don’t assign the flag the global error will be created.
If we forget that the global error message exists and don’t set the flag, the worst case scenario is that an extra message will be displayed.
This is my preferred way of handling this problem.
Third approach, magic with proxies
If we don’t like the flag, we can use another approach to show global messages only if the user don’t catch it, we can use javascript proxies.
The mozilla documentation defines a proxy as:
We can use this object to intercept usages of our exception and show the global message only if the programmer don’t use our exception, like this:
In this example, the console.log
only will be executed if we use an property of the error (in the example err.code
).
This is the most magical approach, the only issue is that we can’t not longer let the global handler manage the error once we use the Exception.
What do you think? How do you handle the global messages?