When programming, we are often afraid of errors. But by working with it, we learn why one should not do this (and the other), as well as how to make the code better.
This article is divided into 3 parts, the first is an overview of the error. We will then focus on the backend (Node.js + Express.js) and finally the error handling in React.js.
I. JavaScript Errors and generic handling
throw new Error('something went wrong')
– will create an instance of Error in JavaScript and stop executing the code until you do something with the Error. When you're first programming JavaScript, you usually won't do it, but only encounter it when using the library (or when executing code), such as ReferenceError: fs is not defined
.
Error Object
Object Error has 2 properties that we can use. One is the message, that is, the argument you pass to the Error constructor, for example new Error('This is the message')
. You can access messages through the messsage
property:
1 2 3 | <span class="token keyword">const</span> myError <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Error</span> <span class="token punctuation">(</span> ‘please improve your code’ <span class="token punctuation">)</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> myError <span class="token punctuation">.</span> message <span class="token punctuation">)</span> <span class="token comment">// please improve your code</span> |
The second extremely important attribute is the stack trace of the Error. You can access it via the stack
property. The stack will show you the history of the files responsible for the error. The stack also contains messages and is followed by the corresponding error and file.
1 2 3 4 5 6 7 8 9 10 11 | Error <span class="token punctuation">:</span> please improve your code at Object <span class="token punctuation">.</span> <span class="token operator"><</span> anonymous <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token operator">/</span> Users <span class="token operator">/</span> gisderdube <span class="token operator">/</span> Documents <span class="token operator">/</span> _projects <span class="token operator">/</span> hacking <span class="token punctuation">.</span> nosync <span class="token operator">/</span> error <span class="token operator">-</span> handling <span class="token operator">/</span> src <span class="token operator">/</span> general <span class="token punctuation">.</span> js <span class="token punctuation">:</span> <span class="token number">1</span> <span class="token punctuation">:</span> <span class="token number">79</span> <span class="token punctuation">)</span> at Module <span class="token punctuation">.</span> <span class="token function">_compile</span> <span class="token punctuation">(</span> internal <span class="token operator">/</span> modules <span class="token operator">/</span> cjs <span class="token operator">/</span> loader <span class="token punctuation">.</span> js <span class="token punctuation">:</span> <span class="token number">689</span> <span class="token punctuation">:</span> <span class="token number">30</span> <span class="token punctuation">)</span> at Object <span class="token punctuation">.</span> Module <span class="token punctuation">.</span> _extensions <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token function">js</span> <span class="token punctuation">(</span> internal <span class="token operator">/</span> modules <span class="token operator">/</span> cjs <span class="token operator">/</span> loader <span class="token punctuation">.</span> js <span class="token punctuation">:</span> <span class="token number">700</span> <span class="token punctuation">:</span> <span class="token number">10</span> <span class="token punctuation">)</span> at Module <span class="token punctuation">.</span> <span class="token function">load</span> <span class="token punctuation">(</span> internal <span class="token operator">/</span> modules <span class="token operator">/</span> cjs <span class="token operator">/</span> loader <span class="token punctuation">.</span> js <span class="token punctuation">:</span> <span class="token number">599</span> <span class="token punctuation">:</span> <span class="token number">32</span> <span class="token punctuation">)</span> at <span class="token function">tryModuleLoad</span> <span class="token punctuation">(</span> internal <span class="token operator">/</span> modules <span class="token operator">/</span> cjs <span class="token operator">/</span> loader <span class="token punctuation">.</span> js <span class="token punctuation">:</span> <span class="token number">538</span> <span class="token punctuation">:</span> <span class="token number">12</span> <span class="token punctuation">)</span> at Function <span class="token punctuation">.</span> Module <span class="token punctuation">.</span> <span class="token function">_load</span> <span class="token punctuation">(</span> internal <span class="token operator">/</span> modules <span class="token operator">/</span> cjs <span class="token operator">/</span> loader <span class="token punctuation">.</span> js <span class="token punctuation">:</span> <span class="token number">530</span> <span class="token punctuation">:</span> <span class="token number">3</span> <span class="token punctuation">)</span> at Function <span class="token punctuation">.</span> Module <span class="token punctuation">.</span> <span class="token function">runMain</span> <span class="token punctuation">(</span> internal <span class="token operator">/</span> modules <span class="token operator">/</span> cjs <span class="token operator">/</span> loader <span class="token punctuation">.</span> js <span class="token punctuation">:</span> <span class="token number">742</span> <span class="token punctuation">:</span> <span class="token number">12</span> <span class="token punctuation">)</span> at <span class="token function">startup</span> <span class="token punctuation">(</span> internal <span class="token operator">/</span> bootstrap <span class="token operator">/</span> node <span class="token punctuation">.</span> js <span class="token punctuation">:</span> <span class="token number">266</span> <span class="token punctuation">:</span> <span class="token number">19</span> <span class="token punctuation">)</span> at <span class="token function">bootstrapNodeJSCore</span> <span class="token punctuation">(</span> internal <span class="token operator">/</span> bootstrap <span class="token operator">/</span> node <span class="token punctuation">.</span> js <span class="token punctuation">:</span> <span class="token number">596</span> <span class="token punctuation">:</span> <span class="token number">3</span> <span class="token punctuation">)</span> |
Throwing and Handling Errors
Because the Error instance does not have any effects, such as new Error ('…') doesn't do anything. When Error throw
n, it becomes more interesting. The code will stop executing until you handle it somewhere. Remember whether you throw
error manually or the library or runtime. Let's see how we can work with errors in different scenarios.
try ... catch
This is the simplest way, but is often overlooked when dealing with errors – fortunately it is now used more often thanks to async / await. It is also used to catch sync errors. For example:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">const</span> a <span class="token operator">=</span> <span class="token number">5</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> b <span class="token punctuation">)</span> <span class="token comment">// b is not defined, so throws an error</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> <span class="token class-name">err</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token comment">// will log the error with the error stack</span> <span class="token punctuation">}</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> a <span class="token punctuation">)</span> <span class="token comment">// still gets executed</span> |
If we do not put console.log(b)
in try ... catch
, the code will stop executing.
… finally Sometimes we need to execute the code no matter what the condition occurs, now you can use the third block as finally
, just add a line after try ... catch
.
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">const</span> a <span class="token operator">=</span> <span class="token number">5</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> b <span class="token punctuation">)</span> <span class="token comment">// b is not defined, so throws an error</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> <span class="token class-name">err</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token comment">// will log the error with the error stack</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> a <span class="token punctuation">)</span> <span class="token comment">// will always get executed</span> <span class="token punctuation">}</span> |
Asynchronous handling – Callbacks
Asynchronous is the subject that you always have to pay attention when working with JavaScript. When you have an asynchronous function, and an error occurs in that function, the code will continue to execute, so no errors will be fired immediately. When handling asynchronous functions with callbacks, you usually get two arguments in the callback, usually like this:
1 2 3 4 5 | <span class="token function">myAsyncFunc</span> <span class="token punctuation">(</span> someInput <span class="token punctuation">,</span> <span class="token punctuation">(</span> err <span class="token punctuation">,</span> result <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token keyword">return</span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token comment">// we will see later what to do with the error object.</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> result <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
If there is an error, the err
parameter will be equivalent to the error. Otherwise, this parameter will be undefined
or null
.
Asynchronous – Promises
A better way to handle asynchronous is to use promises. Besides making the code easier to read, we also improve error handling. No need to worry too much about catching the exact Error, as long as there is a catch
block. When chaining a promise, each block catch
will catch all the errors from executing the promise or from the last block catch
. Note that promises without catch
will not be able to terminate
code, but you will get a more difficult message like this:
1 2 3 | <span class="token punctuation">(</span> node <span class="token punctuation">:</span> <span class="token number">7741</span> <span class="token punctuation">)</span> UnhandledPromiseRejectionWarning <span class="token punctuation">:</span> Unhandled promise <span class="token function">rejection</span> <span class="token punctuation">(</span> rejection id <span class="token punctuation">:</span> <span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> Error <span class="token punctuation">:</span> something went <span class="token function">wrong</span> <span class="token punctuation">(</span> node <span class="token punctuation">:</span> <span class="token number">7741</span> <span class="token punctuation">)</span> DeprecationWarning <span class="token punctuation">:</span> Unhandled promise rejections are deprecated <span class="token punctuation">.</span> In the future <span class="token punctuation">,</span> promise rejections that are not handled will terminate the Node <span class="token punctuation">.</span> js process <span class="token keyword">with</span> a non <span class="token operator">-</span> zero exit code <span class="token punctuation">.</span> |
So always add a catch
block for promises. As follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | Promise <span class="token punctuation">.</span> <span class="token function">resolve</span> <span class="token punctuation">(</span> <span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> res <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> res <span class="token punctuation">)</span> <span class="token comment">// 1</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span> <span class="token punctuation">(</span> <span class="token string">'something went wrong'</span> <span class="token punctuation">)</span> <span class="token keyword">return</span> Promise <span class="token punctuation">.</span> <span class="token function">resolve</span> <span class="token punctuation">(</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> res <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> res <span class="token punctuation">)</span> <span class="token comment">// will not get executed</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> err <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token comment">// we will see what to do with it later</span> <span class="token keyword">return</span> Promise <span class="token punctuation">.</span> <span class="token function">resolve</span> <span class="token punctuation">(</span> <span class="token number">3</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> res <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> res <span class="token punctuation">)</span> <span class="token comment">// 3</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> err <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// in case in the previous block occurs another error</span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
try … catch – again
With the introduction of async / await in JavaScript, we return to the classic way of catching errors, with try ... catch ... finally
, quite gently:
1 2 3 4 5 6 7 8 9 10 | <span class="token punctuation">;</span> <span class="token punctuation">(</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">await</span> <span class="token function">someFuncThatThrowsAnError</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> <span class="token class-name">err</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token comment">// we will make sense of that later</span> <span class="token punctuation">}</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">'Easy!'</span> <span class="token punctuation">)</span> <span class="token comment">// will get executed</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> |
II. Error handling in the Server
Now that we have the tools to work with the Error, let's look at how we will actually do it. Error handling in the backend is part of your application. There are many approaches to this problem, I will show you how to approach custom Error constructor and Error codes, which we can easily pass to the frontend or API.
We will use the Express.js framework as routing. Think about the structure we want to catch the most effectively. We want:
- General error handling, for example:
Something went wrong, please try again or contact us.
. This approach is not so good, but at least it notifies the user that something is wrong, instead of infinite loading screen. - Specific error handling to give users detailed information about the error and how to handle it, such as some missing information, imported content already in the database, …
Building custom Error constructor
We will use the Error constructor and extend it. Inheritance is a risky alternative in JavaScript, but this time it's very useful. Why do we need it? Because we still want to stack trace to debug more efficiently. We just need to add the code
, which we will access later via err.code
, as well as the status to pass to the frontend.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">class</span> <span class="token class-name">CustomError</span> <span class="token keyword">extends</span> <span class="token class-name">Error</span> <span class="token punctuation">{</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> code <span class="token operator">=</span> <span class="token string">'GENERIC'</span> <span class="token punctuation">,</span> status <span class="token operator">=</span> <span class="token number">500</span> <span class="token punctuation">,</span> <span class="token operator">...</span> params <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">(</span> <span class="token operator">...</span> params <span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> Error <span class="token punctuation">.</span> captureStackTrace <span class="token punctuation">)</span> <span class="token punctuation">{</span> Error <span class="token punctuation">.</span> <span class="token function">captureStackTrace</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">,</span> CustomError <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> code <span class="token operator">=</span> code <span class="token keyword">this</span> <span class="token punctuation">.</span> status <span class="token operator">=</span> status <span class="token punctuation">}</span> <span class="token punctuation">}</span> module <span class="token punctuation">.</span> exports <span class="token operator">=</span> CustomError |
Process routing
As mentioned, we want a single point of truth
to handle errors, which means every route has the same error handling. By default express is not supported because the route is packaged.
To solve this problem, we can install a route handler and define the logic as a regular function. That way, when the routing function throw error, it will be returned to the route handler, then passed to the frontend. Whenever an error occurs in the backend, we want to pass the response to the frontend, assuming it is a JSON API in the following format:
1 2 3 4 5 | <span class="token punctuation">{</span> error <span class="token punctuation">:</span> <span class="token string">'SOME_ERROR_CODE'</span> <span class="token punctuation">,</span> description <span class="token punctuation">:</span> <span class="token string">'Something bad happened. Please try again or contact support.'</span> <span class="token punctuation">}</span> |
The route handler looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | <span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'express'</span> <span class="token punctuation">)</span> <span class="token keyword">const</span> router <span class="token operator">=</span> express <span class="token punctuation">.</span> <span class="token function">Router</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">const</span> CustomError <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'../CustomError'</span> <span class="token punctuation">)</span> router <span class="token punctuation">.</span> <span class="token function">use</span> <span class="token punctuation">(</span> <span class="token keyword">async</span> <span class="token punctuation">(</span> req <span class="token punctuation">,</span> res <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> route <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token template-string"><span class="token string">`.</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> req <span class="token punctuation">.</span> path <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">`</span></span> <span class="token punctuation">)</span> <span class="token punctuation">[</span> req <span class="token punctuation">.</span> method <span class="token punctuation">]</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> result <span class="token operator">=</span> <span class="token function">route</span> <span class="token punctuation">(</span> req <span class="token punctuation">)</span> <span class="token comment">// We pass the request to the route function</span> res <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> result <span class="token punctuation">)</span> <span class="token comment">// We just send to the client what we get returned from the route function</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> <span class="token class-name">err</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* This will be entered, if an error occurs inside the route function. */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> err <span class="token keyword">instanceof</span> <span class="token class-name">CustomError</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* In case the error has already been handled, we just transform the error to our return object. */</span> <span class="token keyword">return</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> err <span class="token punctuation">.</span> status <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> error <span class="token punctuation">:</span> err <span class="token punctuation">.</span> code <span class="token punctuation">,</span> description <span class="token punctuation">:</span> err <span class="token punctuation">.</span> message <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token comment">// For debugging reasons</span> <span class="token comment">// It would be an unhandled error, here we can just return our generic error object.</span> <span class="token keyword">return</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">500</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> error <span class="token punctuation">:</span> <span class="token string">'GENERIC'</span> <span class="token punctuation">,</span> description <span class="token punctuation">:</span> <span class="token string">'Something went wrong. Please try again or contact support.'</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> <span class="token class-name">err</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* This will be entered, if the require fails, meaning there is either no file with the name of the request path or no exported function with the given request method. */</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">404</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> error <span class="token punctuation">:</span> <span class="token string">'NOT_FOUND'</span> <span class="token punctuation">,</span> description <span class="token punctuation">:</span> <span class="token string">'The resource you tried to access does not exist.'</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> module <span class="token punctuation">.</span> exports <span class="token operator">=</span> router |
Let's see what the routing file will look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <span class="token keyword">const</span> CustomError <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'../CustomError'</span> <span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token function-variable function">GET</span> <span class="token operator">=</span> req <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// example for success</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> name <span class="token punctuation">:</span> <span class="token string">'Rio de Janeiro'</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> <span class="token function-variable function">POST</span> <span class="token operator">=</span> req <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// example for unhandled error</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span> <span class="token punctuation">(</span> <span class="token string">'Some unexpected error, may also be thrown by a library or the runtime.'</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> <span class="token function-variable function">DELETE</span> <span class="token operator">=</span> req <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// example for handled error</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">CustomError</span> <span class="token punctuation">(</span> <span class="token string">'CITY_NOT_FOUND'</span> <span class="token punctuation">,</span> <span class="token number">404</span> <span class="token punctuation">,</span> <span class="token string">'The city you are trying to delete could not be found.'</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> <span class="token function-variable function">PATCH</span> <span class="token operator">=</span> req <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// example for catching errors and using a CustomError</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// something bad happens here</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span> <span class="token punctuation">(</span> <span class="token string">'Some internal error'</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> <span class="token class-name">err</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token comment">// decide what you want to do here</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">CustomError</span> <span class="token punctuation">(</span> <span class="token string">'CITY_NOT_EDITABLE'</span> <span class="token punctuation">,</span> <span class="token number">400</span> <span class="token punctuation">,</span> <span class="token string">'The city you are trying to edit is not editable.'</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> module <span class="token punctuation">.</span> exports <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token constant">GET</span> <span class="token punctuation">,</span> <span class="token constant">POST</span> <span class="token punctuation">,</span> <span class="token constant">DELETE</span> <span class="token punctuation">,</span> <span class="token constant">PATCH</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> |
Here I don't handle the request, I just added some error scenarios. Basically you will have an unresolved error, the frontend will get:
1 2 3 4 5 | <span class="token punctuation">{</span> error <span class="token punctuation">:</span> <span class="token string">'GENERIC'</span> <span class="token punctuation">,</span> description <span class="token punctuation">:</span> <span class="token string">'Something went wrong. Please try again or contact support.'</span> <span class="token punctuation">}</span> |
Or you will throw a CustomError
:
1 2 | <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">CustomError</span> <span class="token punctuation">(</span> <span class="token string">'MY_CODE'</span> <span class="token punctuation">,</span> <span class="token number">400</span> <span class="token punctuation">,</span> <span class="token string">'Error description'</span> <span class="token punctuation">)</span> |
will become
1 2 3 4 5 | <span class="token punctuation">{</span> error <span class="token punctuation">:</span> <span class="token string">'MY_CODE'</span> <span class="token punctuation">,</span> description <span class="token punctuation">:</span> <span class="token string">'Error description'</span> <span class="token punctuation">}</span> |
III. Show error to the user
The final step is to manage the errors in the frontend. You want to handle errors in both the frontend as well as the backend. First let's see how we display the error. As connected, I will use React to illustrate.
Save Errors in React state
Errors and messages can change, so you need to put them in component state. By default, the error will be reset so the first time the user view will not see the error.
Next, we need to distinguish the different types of errors. There are 3 types:
- Global Error, for example generic error in backend or non-logged in user …
- Specific errors from backend, such as incorrect password …
- Specific errors from the frontend, such as input validation errors that have not been entered
Global Errors
Usually I save these errors in the parent component and render static UI.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <span class="token keyword">import</span> React <span class="token punctuation">,</span> <span class="token punctuation">{</span> Component <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span> <span class="token keyword">import</span> GlobalError <span class="token keyword">from</span> <span class="token string">'./GlobalError'</span> <span class="token keyword">class</span> <span class="token class-name">Application</span> <span class="token keyword">extends</span> <span class="token class-name">Component</span> <span class="token punctuation">{</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> props <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">(</span> props <span class="token punctuation">)</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> state <span class="token operator">=</span> <span class="token punctuation">{</span> error <span class="token punctuation">:</span> <span class="token string">''</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> _resetError <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> _resetError <span class="token punctuation">.</span> <span class="token function">bind</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">)</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> _setError <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> _setError <span class="token punctuation">.</span> <span class="token function">bind</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">render</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> div className <span class="token operator">=</span> <span class="token string">"container"</span> <span class="token operator">></span> <span class="token operator"><</span> GlobalError error <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> state <span class="token punctuation">.</span> error <span class="token punctuation">}</span> resetError <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> _resetError <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token operator"><</span> h1 <span class="token operator">></span> Handling Errors <span class="token operator"><</span> <span class="token operator">/</span> h1 <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">_resetError</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">setState</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> error <span class="token punctuation">:</span> <span class="token string">''</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">_setError</span> <span class="token punctuation">(</span> newError <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">setState</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> error <span class="token punctuation">:</span> newError <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> Application |
As you can see, we have an error in the state. We also have a method to reset and change the value of the error. We pass the value and reset it into the GlobalError
component, which will display and reset the error when the user clicks the x:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <span class="token keyword">import</span> React <span class="token punctuation">,</span> <span class="token punctuation">{</span> Component <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span> <span class="token keyword">class</span> <span class="token class-name">GlobalError</span> <span class="token keyword">extends</span> <span class="token class-name">Component</span> <span class="token punctuation">{</span> <span class="token function">render</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> props <span class="token punctuation">.</span> error <span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">null</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> div style <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">{</span> position <span class="token punctuation">:</span> <span class="token string">'fixed'</span> <span class="token punctuation">,</span> top <span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">,</span> left <span class="token punctuation">:</span> <span class="token string">'50%'</span> <span class="token punctuation">,</span> transform <span class="token punctuation">:</span> <span class="token string">'translateX(-50%)'</span> <span class="token punctuation">,</span> padding <span class="token punctuation">:</span> <span class="token number">10</span> <span class="token punctuation">,</span> backgroundColor <span class="token punctuation">:</span> <span class="token string">'#ffcccc'</span> <span class="token punctuation">,</span> boxShadow <span class="token punctuation">:</span> <span class="token string">'0 3px 25px -10px rgba(0,0,0,0.5)'</span> <span class="token punctuation">,</span> display <span class="token punctuation">:</span> <span class="token string">'flex'</span> <span class="token punctuation">,</span> alignItems <span class="token punctuation">:</span> <span class="token string">'center'</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> props <span class="token punctuation">.</span> error <span class="token punctuation">}</span> <span class="token operator">&</span> nbsp <span class="token punctuation">;</span> <span class="token operator"><</span> i className <span class="token operator">=</span> <span class="token string">"material-icons"</span> style <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">{</span> cursor <span class="token punctuation">:</span> <span class="token string">'pointer'</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> onClick <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> props <span class="token punctuation">.</span> resetError <span class="token punctuation">}</span> <span class="token operator">></span> close <span class="token operator"><</span> <span class="token operator">/</span> i <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> GlobalError |
In line 5, we do not render because there is no error. You can now use the global error state anywhere, such as when a request from the backend returns an error: 'GENERIC'
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <span class="token keyword">import</span> React <span class="token punctuation">,</span> <span class="token punctuation">{</span> Component <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span> <span class="token keyword">import</span> axios <span class="token keyword">from</span> <span class="token string">'axios'</span> <span class="token keyword">class</span> <span class="token class-name">GenericErrorReq</span> <span class="token keyword">extends</span> <span class="token class-name">Component</span> <span class="token punctuation">{</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> props <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">(</span> props <span class="token punctuation">)</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> _callBackend <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> _callBackend <span class="token punctuation">.</span> <span class="token function">bind</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">render</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> div <span class="token operator">></span> <span class="token operator"><</span> button onClick <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> _callBackend <span class="token punctuation">}</span> <span class="token operator">></span> Click me to call the backend <span class="token operator"><</span> <span class="token operator">/</span> button <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">_callBackend</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> axios <span class="token punctuation">.</span> <span class="token function">post</span> <span class="token punctuation">(</span> <span class="token string">'/api/city'</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> result <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// do something with it, if the request is successful</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> err <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> err <span class="token punctuation">.</span> response <span class="token punctuation">.</span> data <span class="token punctuation">.</span> error <span class="token operator">===</span> <span class="token string">'GENERIC'</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> props <span class="token punctuation">.</span> <span class="token function">setError</span> <span class="token punctuation">(</span> err <span class="token punctuation">.</span> response <span class="token punctuation">.</span> data <span class="token punctuation">.</span> description <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> GenericErrorReq |
Handling specific errors
Similar to the global error, we have local error state in the component.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <span class="token keyword">import</span> React <span class="token punctuation">,</span> <span class="token punctuation">{</span> Component <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span> <span class="token keyword">import</span> axios <span class="token keyword">from</span> <span class="token string">'axios'</span> <span class="token keyword">import</span> InlineError <span class="token keyword">from</span> <span class="token string">'./InlineError'</span> <span class="token keyword">class</span> <span class="token class-name">SpecificErrorRequest</span> <span class="token keyword">extends</span> <span class="token class-name">Component</span> <span class="token punctuation">{</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> props <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">(</span> props <span class="token punctuation">)</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> state <span class="token operator">=</span> <span class="token punctuation">{</span> error <span class="token punctuation">:</span> <span class="token string">''</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> _callBackend <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> _callBackend <span class="token punctuation">.</span> <span class="token function">bind</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">render</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> div <span class="token operator">></span> <span class="token operator"><</span> button onClick <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> _callBackend <span class="token punctuation">}</span> <span class="token operator">></span> Delete your city <span class="token operator"><</span> <span class="token operator">/</span> button <span class="token operator">></span> <span class="token operator"><</span> InlineError error <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> state <span class="token punctuation">.</span> error <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">_callBackend</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">setState</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> error <span class="token punctuation">:</span> <span class="token string">''</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> axios <span class="token punctuation">.</span> <span class="token keyword">delete</span> <span class="token punctuation">(</span> <span class="token string">'/api/city'</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> result <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// do something with it, if the request is successful</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> err <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> err <span class="token punctuation">.</span> response <span class="token punctuation">.</span> data <span class="token punctuation">.</span> error <span class="token operator">===</span> <span class="token string">'GENERIC'</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> props <span class="token punctuation">.</span> <span class="token function">setError</span> <span class="token punctuation">(</span> err <span class="token punctuation">.</span> response <span class="token punctuation">.</span> data <span class="token punctuation">.</span> description <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">setState</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> error <span class="token punctuation">:</span> err <span class="token punctuation">.</span> response <span class="token punctuation">.</span> data <span class="token punctuation">.</span> description <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> SpecificErrorRequest |
Error internationalization with error code
You may wonder what error codes like GENERIC
do. As the application grows, you may need to support multiple languages, then returning messages based on the user's language will be simpler when matching error code.
Hopefully you've got a new perspective on error handling. console.error(err)
has become a thing of the past. It is quite convenient to debug but takes time to build for production, it is best to use a logging library.