Translated from the source
Troubleshooting isn’t just about reducing developer time to find bugs, it’s about building a codebase that scales with your system.
Error Types in NodeJS
In Node.js there are 2 main types of errors:
- Operational errors : runtime error, some examples: “Out of memory”, “An invalid input for an API endpoint”
- Programmer errors : unexpected bugs, the code itself has problems to solve. Typical example: reading the property of “undefined” object. These bugs are often created by devs and not related to operations.
Error handling
With the errors already defined by Node.js, it is easy to track the information around it thanks to Stacktrace
→ from which we can find the root cause of the error.
In addition, extending from Error class
as well as adding other properties such as HTTP status code
will also help information about errors become more detailed.
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 | <span class="token keyword">class</span> <span class="token class-name">BaseError</span> <span class="token keyword">extends</span> <span class="token class-name">Error</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">readonly</span> name <span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">readonly</span> httpCode <span class="token operator">:</span> HttpStatusCode <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">readonly</span> isOperational <span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">;</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> name <span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">,</span> httpCode <span class="token operator">:</span> HttpStatusCode <span class="token punctuation">,</span> description <span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">,</span> isOperational <span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">,</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">(</span> description <span class="token punctuation">)</span> <span class="token punctuation">;</span> Object <span class="token punctuation">.</span> <span class="token function">setPrototypeOf</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token punctuation">.</span> target <span class="token punctuation">.</span> prototype <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> name <span class="token operator">=</span> name <span class="token punctuation">;</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> httpCode <span class="token operator">=</span> httpCode <span class="token punctuation">;</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> isOperational <span class="token operator">=</span> isOperational <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> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// free to extend from BaseError</span> <span class="token keyword">class</span> <span class="token class-name">APIError</span> <span class="token keyword">extends</span> <span class="token class-name">BaseError</span> <span class="token punctuation">{</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> name <span class="token punctuation">,</span> httpCode <span class="token operator">=</span> HttpStatusCode <span class="token punctuation">.</span> <span class="token constant">INTERNAL_SERVER</span> <span class="token punctuation">,</span> isOperational <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token punctuation">,</span> description <span class="token operator">=</span> <span class="token string">'Internal Server Error'</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">(</span> name <span class="token punctuation">,</span> httpCode <span class="token punctuation">,</span> isOperational <span class="token punctuation">,</span> descritpion <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Some basic httpStatusCode
can be added here:
1 2 3 4 5 6 7 | <span class="token keyword">export</span> <span class="token keyword">enum</span> HttpStatusCode <span class="token punctuation">{</span> <span class="token constant">OK</span> <span class="token operator">=</span> <span class="token number">200</span> <span class="token punctuation">,</span> <span class="token constant">BAD_REQUEST</span> <span class="token operator">=</span> <span class="token number">400</span> <span class="token punctuation">,</span> <span class="token constant">NOT_FOUND</span> <span class="token operator">=</span> <span class="token number">404</span> <span class="token punctuation">,</span> <span class="token constant">INTERNAL_SERVER</span> <span class="token operator">=</span> <span class="token number">500</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> |
Usage is as follows:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token keyword">await</span> User <span class="token punctuation">.</span> <span class="token function">getById</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 keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span> user <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">APIError</span> <span class="token punctuation">(</span> <span class="token string">'NOT FOUND'</span> <span class="token punctuation">,</span> HttpStatusCode <span class="token punctuation">.</span> <span class="token constant">NOT_FOUND</span> <span class="token punctuation">,</span> <span class="token boolean">true</span> <span class="token punctuation">,</span> <span class="token string">'detailed explanation'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Centralized error handling with NodeJS
Building a component with functionality to handle errors will help reduce the amount of duplication of error handling code in the project. This component is responsible for making catching errors easier to understand for example:
- Send notification to system admin
- Pass error events to a monitoring service like Sentry.io and log them out
Before being sent to error-handling centralized, errors are sent to error-handling middleware to phân biệt giữa các error types
.
1 2 3 4 5 6 7 8 | <span class="token keyword">try</span> <span class="token punctuation">{</span> userSerivce <span class="token punctuation">.</span> <span class="token function">addNewUser</span> <span class="token punctuation">(</span> req <span class="token punctuation">.</span> body <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> newUser <span class="token operator">:</span> User <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">200</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">json</span> <span class="token punctuation">(</span> newUser <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 function">catch</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> error <span class="token operator">:</span> Error <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">next</span> <span class="token punctuation">(</span> error <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> |
And Error-handling centralized would look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">ErrorHandler</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">async</span> <span class="token function">handleError</span> <span class="token punctuation">(</span> err <span class="token operator">:</span> Error <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token builtin">Promise</span> <span class="token operator"><</span> <span class="token keyword">void</span> <span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">await</span> logger <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> <span class="token string">'Error message from the centralized error-handling component'</span> <span class="token punctuation">,</span> err <span class="token punctuation">,</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> <span class="token function">sendToSlack</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> <span class="token function">sendEventsToSentry</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">public</span> <span class="token function">isTrustedError</span> <span class="token punctuation">(</span> error <span class="token operator">:</span> Error <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> error instanceOf BaseError <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> error <span class="token punctuation">.</span> isOperational <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token boolean">false</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
To make it easier for developers to track bugs, log errors out in an easy-to-see format.
Some typical formatter loggers are:
These two libraries will help provide logs in different level formats depending on the level of the error.
With Programmer errors
, the best solution is B1. Crash app instantly B2. Restart the app with tools like pm2
This is because Programmer errors
will often cause the app to end up in an unexpected state.
1 2 3 4 5 6 7 | process <span class="token punctuation">.</span> <span class="token function">on</span> <span class="token punctuation">(</span> <span class="token string">'uncaughtException'</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> error <span class="token operator">:</span> Error <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> errorHandler <span class="token punctuation">.</span> <span class="token function">handleError</span> <span class="token punctuation">(</span> error <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> errorHandler <span class="token punctuation">.</span> <span class="token function">isTrustedError</span> <span class="token punctuation">(</span> error <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> process <span class="token punctuation">.</span> <span class="token function">exit</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 punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
With offers rejection we can do the following:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token comment">// get the unhandled rejection and throw it to another fallback handler we already have.</span> process <span class="token punctuation">.</span> <span class="token function">on</span> <span class="token punctuation">(</span> <span class="token string">'unhandledRejection'</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> reason <span class="token operator">:</span> Error <span class="token punctuation">,</span> promise <span class="token operator">:</span> <span class="token builtin">Promise</span> <span class="token operator"><</span> <span class="token builtin">any</span> <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> reason <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> process <span class="token punctuation">.</span> <span class="token function">on</span> <span class="token punctuation">(</span> <span class="token string">'uncaughtException'</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> error <span class="token operator">:</span> Error <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> errorHandler <span class="token punctuation">.</span> <span class="token function">handleError</span> <span class="token punctuation">(</span> error <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> errorHandler <span class="token punctuation">.</span> <span class="token function">isTrustedError</span> <span class="token punctuation">(</span> error <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> process <span class="token punctuation">.</span> <span class="token function">exit</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 punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |