Node Js Request Status Code Then Send Again

It is non difficult to see that some people are struggling to handle errors, and some are even totally missing it. Handling errors properly means not merely reducing the development fourth dimension by finding bugs and errors easily but also developing a robust codebase for big-scale applications.

In particular, Node.js developers sometimes find themselves working with not-so-make clean code while handling various kinds of errors, incorrectly applying the aforementioned logic everywhere to deal with them. They only keep request themselves "Is Node.js bad at handling errors?" or If non, how to handle them?" My answer to them is "No, Node.js is dandy at all. That depends on the states developers."

Here is ane of my favorite solutions for that.

Types of Errors in Node.js

First of all, information technology is necessary to take a clear understanding of errors in Node.js. In full general, Node.js errors are divided into ii singled-out categories: operational errors and programmer errors.

  • Operational errors represent runtime issues whose results are expected and should exist dealt with in a proper way. Operational errors don't mean the awarding itself has bugs, but developers need to handle them thoughtfully. Examples of operational errors include "out of memory," "an invalid input for an API endpoint," and and so on.
  • Developer errors stand for unexpected bugs in poorly written code. They mean the code itself has some problems to solve and was coded wrong. A proficient example is to endeavor to read a belongings of "undefined." To fix the consequence, the code has to exist inverse. That is a problems a developer made, not an operational error.

With that in mind, you should have no problem distinguishing between these two categories of errors: Operational errors are a natural office of an application, and programmer errors are bugs acquired past developers. A logical question that follows is: "Why is information technology useful to carve up them into two categories and bargain with them?"

Without a clear understanding of errors, you might feel like restarting an application whenever an error occurs. Does it make sense to restart an application due to "File Not Institute" errors when thousands of users are enjoying the application? Absolutely not.

But what virtually developer errors? Does it make sense to keep an application running when an unknown problems appears that could result in an unexpected snowball outcome in the awarding? Again, definitely not!

It's Time to Handle Errors Properly

Assuming you have some feel with async JavaScript and Node.js, you might accept experienced drawbacks when using callbacks for dealing with errors. They strength yous to bank check errors all the way down to nested ones, causing notorious "callback hell" issues that make it hard to follow the code flow.

Using promises or async/await is a good replacement for callbacks. The typical lawmaking flow of async/await looks similar the following:

          const doAsyncJobs = async () => {  try {    const result1 = await job1();    const result2 = look job2(result1);    const result3 = expect job3(result2);    return await job4(result3);  } grab (mistake) {    panel.error(error);  } finally {    await anywayDoThisJob();  } }                  

Using Node.js built-in Error object is a adept practice considering it includes intuitive and clear data nigh errors like StackTrace, which almost developers depend on to keep track of the root of an error. And additional meaningful backdrop like HTTP status code and a description by extending the Error class will brand it more than informative.

          grade BaseError extends Error {  public readonly name: string;  public readonly httpCode: HttpStatusCode;  public readonly isOperational: boolean;    constructor(proper noun: string, httpCode: HttpStatusCode, description: string, isOperational: boolean) {    super(clarification);    Object.setPrototypeOf(this, new.target.epitome);      this.name = name;    this.httpCode = httpCode;    this.isOperational = isOperational;      Error.captureStackTrace(this);  } }  //gratis to extend the BaseError grade APIError extends BaseError {  constructor(proper name, httpCode = HttpStatusCode.INTERNAL_SERVER, isOperational = true, description = 'internal server error') {    super(name, httpCode, isOperational, description);  } }                  

I only implemented some HTTP status codes for the sake of simplicity, just you lot are complimentary to add more than later.

          export enum HttpStatusCode {  OK = 200,  BAD_REQUEST = 400,  NOT_FOUND = 404,  INTERNAL_SERVER = 500, }                  

There is no need to extend BaseError or APIError, but it is okay to extend it for common errors according to your needs and personal preferences.

          form HTTP400Error extends BaseError {  constructor(description = 'bad asking') {    super('Not FOUND', HttpStatusCode.BAD_REQUEST, true, description);  } }                  

So how do you utilise it? Just throw this in:

          ... const user = await User.getUserById(1); if (user === zero)  throw new APIError(    'Non FOUND',    HttpStatusCode.NOT_FOUND,    true,    'detailed explanation'  );                  

Centralized Node.js Fault-treatment

Now, we are ready to build the main component of our Node.js fault-handling system: the centralized error-handling component.

It is unremarkably a good thought to build a centralized error-handling component in society to avoid possible lawmaking duplications when handling errors. The error-treatment component is in charge of making the caught errors understandable by, for example, sending notifications to organization admins (if necessary), transferring events to a monitoring service similar Sentry.io, and logging them.

Here is a basic workflow for dealing with errors:

Error handling in Node.js: basic workflow

In some parts of the code, errors are caught to transfer to an error-handling middleware.

          ... try {  userService.addNewUser(req.body).then((newUser: User) => {    res.status(200).json(newUser);  }).grab((fault: Error) => {    next(error)  }); } catch (error) {  side by side(error); } ...                  

The mistake-handling middleware is a good identify to distinguish between error types and send them to the centralized mistake-handling component. Knowing the basics about handling errors in Express.js middleware would certainly help.

          app.utilise(async (err: Error, req: Asking, res: Response, next: NextFunction) => {  if (!errorHandler.isTrustedError(err)) {    next(err);  }  await errorHandler.handleError(err); });                  

By at present, one can imagine what the centralized component should look similar considering we have already used some of its functions. Carry in mind that it is totally upward to y'all how to implement it, just it might look similar the post-obit:

          form ErrorHandler {  public async handleError(err: Error): Promise<void> {    look logger.error(      'Mistake bulletin from the centralized error-handling component',      err,    );    await sendMailToAdminIfCritical();    wait sendEventsToSentry();  }    public isTrustedError(mistake: Error) {    if (error instanceof BaseError) {      render error.isOperational;    }    return imitation;  } } export const errorHandler = new ErrorHandler();                  

Sometimes, the output of the default "console.log" makes it difficult to keep rails of errors. Rather, it could be much better to print errors in a formatted manner and then that developers can quickly understand the issues and make sure they are fixed.

Overall, this will save developers time making it like shooting fish in a barrel to keep track of errors and handle them past increasing their visibility. It is a practiced decision to employ a customizable logger like winston or morgan.

Hither is a customized winston logger:

          const customLevels = {  levels: {    trace: 5,    debug: iv,    info: 3,    warn: 2,    error: i,    fatal: 0,  },  colors: {    trace: 'white',    debug: 'greenish',    info: 'green',    warn: 'yellow',    error: 'red',    fatal: 'reddish',  }, };   const formatter = winston.format.combine(  winston.format.colorize(),  winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),  winston.format.splat(),  winston.format.printf((info) => {    const { timestamp, level, message, ...meta } = info;      return `${timestamp} [${level}]: ${message} ${      Object.keys(meta).length ? JSON.stringify(meta, null, ii) : ''    }`;  }), );   class Logger {  private logger: winston.Logger;    constructor() {    const prodTransport = new winston.transports.File({      filename: 'logs/error.log',      level: 'fault',    });    const send = new winston.transports.Console({      format: formatter,    });    this.logger = winston.createLogger({      level: isDevEnvironment() ? 'trace' : 'error',      levels: customLevels.levels,      transports: [isDevEnvironment() ? transport : prodTransport],    });    winston.addColors(customLevels.colors);  }    trace(msg: any, meta?: whatever) {    this.logger.log('trace', msg, meta);  }    debug(msg: any, meta?: any) {    this.logger.debug(msg, meta);  }    info(msg: any, meta?: whatsoever) {    this.logger.info(msg, meta);  }    warn(msg: any, meta?: any) {    this.logger.warn(msg, meta);  }    error(msg: any, meta?: any) {    this.logger.error(msg, meta);  }    fatal(msg: any, meta?: any) {    this.logger.log('fatal', msg, meta);  } }   export const logger = new Logger();                  

What it basically provides is logging at multiple different levels in a formatted way, with clear colors, and logging into dissimilar output media according to the runtime environs. The proficient thing with this is you tin spotter and query logs past using winston'southward built-in APIs. Furthermore, you lot can employ a log analysis tool to analyze the formatted log files to get more useful data about the application. It'south awesome, isn't it?

Up to this betoken, we mostly discussed dealing with operational errors. How most programmer errors? The best style to deal with these errors is to crash immediately and restart gracefully with an automatic restarter similar PM2—the reason beingness that programmer errors are unexpected, as they are actual bugs that might cause the application to end upwards in a incorrect state and behave in an unexpected style.

          process.on('uncaughtException', (error: Fault) => {  errorHandler.handleError(error);  if (!errorHandler.isTrustedError(error)) {    process.exit(1);  } });                  

Last just not least, I am going to mention dealing with unhandled hope rejections and exceptions.

You might observe yourself spending a lot of time dealing with promises when working on Node.js/Limited applications. Information technology is not hard to encounter alert messages about unhandled promise rejections when you forget to handle rejections.

The warning letters don't do much except logging, but it is a good practice to use a decent fallback and subscribe to process.on('unhandledRejection', callback).

The typical mistake-handling period might look like the following:

          // somewhere in the code ... User.getUserById(i).so((firstUser) => {   if (firstUser.isSleeping === false) throw new Error('He is not sleeping!'); }); ...   // get the unhandled rejection and throw it to another fallback handler we already have. process.on('unhandledRejection', (reason: Error, promise: Hope<whatever>) => {  throw reason; });   process.on('uncaughtException', (fault: Error) => {  errorHandler.handleError(error);  if (!errorHandler.isTrustedError(error)) {    process.exit(1);  } });                  

Wrapping Up

When all is said and done, yous should realize that fault-handling is not an optional actress but rather an essential part of an application, both in the evolution stage and in production.

The strategy of handling errors in a single component in Node.js volition ensure developers save valuable time and write clean and maintainable code by avoiding code duplication and missing mistake context.

I hope you enjoyed reading this commodity and institute the discussed mistake-handling workflow and implementation helpful for building a robust codebase in Node.js.


Farther Reading on the Toptal Engineering science Blog:

  • Using Express.js Routes for Promise-based Error Handling

thackertreff1945.blogspot.com

Source: https://www.toptal.com/nodejs/node-js-error-handling

0 Response to "Node Js Request Status Code Then Send Again"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel