proper way of handling fatal errors in Node.js

Viewed 1411

According to Node.js documentation usually, it's not a good idea to use process.exit() because async IO operations like console.log() or other logging methods (Pino logging library in my case) might get skipped, the process exits before they complete their task. I was wondering what is the best way to handle errors inside a function. My final goal is to handle errors in a function and if the error is a fatal error then exit the process.

I wrote a simplified version of what I currently think is the best option (similar to the solution explained in Node.js docs):

const validateUserInput = (input) => {
  try {
    if (input === 'wrong') { throw new Error('sample error'); } // simplified
  } catch (err) {
    console.log('last message');
    // process.exit(1) --> using this might skip IO operation like the log on previous line
    process.exitCode = 1;
    return false;
  }
  return true;
};

if (validateUserInput('wrong')) {
  // rest of the code
}

// nothing should be written here
2 Answers

SIGTERM is the signal that tells a process to gracefully terminate. It is the signal that's sent from process managers like upstart or supervisord and many others.

You can send this signal from inside the program, in another function: read more

process.kill(process.pid, 'SIGTERM')

You can also setup your own listeners for this event, and perform other tasks.

process.on('SIGTERM', () => {
  server.close(() => {
    console.log('Shutting down the application ...')
  })
})

But SIGTERM is not supported on windows, so you can use SIGINT instead, here is a useful info about SIGINT

After the SIGINT signal is received, the Node.js server initiates a shutdown sequence and enters shutdown mode. All of the existing requests are completed and no new requests are entertained. When the last request completes, the server closes all of the connections and shuts down. read more

Mor about processes here

If a fatal error occurs, the application will need to be terminated gracefully to prevent default behavior from happening and allow additional statements to execute first. You can achieve this by creating a simple event listener:

// For app termination
const gracefulShutdown = (msg, callback) => {
    server.close(() => {
        //execute additional code here
        console.log(`Application disconnected through ${msg}`);
        callback();
    })
}

process.on('SIGINT', () => {
    //execute additional code here
    gracefulShutdown('app termination', () => {
        process.exitCode = 1;
    })
})

Related