DEV Community

Alifa Ara Heya
Alifa Ara Heya

Posted on

Gracefully Handling Server Errors in Node.js with Code Examples

Just wanted to share a quick note on how we're making our Node.js applications more robust by implementing comprehensive error handling. It's crucial for maintaining a stable and reliable server.

Here's a look at some of the common types of server errors we explicitly handle, along with the code we use:

1. Unhandled Rejection Errors (Promises)

These occur when a Promise is rejected but there's no .catch() handler to deal with the error. If left unhandled, it can crash your application. We listen for the unhandledRejection event to gracefully shut down the server, ensuring no lingering processes.

process.on("unhandledRejection", (err) => {
    console.log('Unhandled Rejection detected! Server shutting down..', err);

    // Close the server if it's running
    if (server) {
        server.close(() => {
            process.exit(1); // Exit with a failure code
        });
    }

    process.exit(1); // Exit immediately if server isn't active
});

// Example of an unhandled rejection (you'd typically remove this in production)
// Promise.reject(new Error("I forgot to catch this promise"));
Enter fullscreen mode Exit fullscreen mode

2. Uncaught Exception Errors (Synchronous Code)

These are synchronous errors that are not caught by a try...catch block. Think of things like referencing an undefined variable or a typo that leads to a runtime error. The uncaughtException event helps us catch these, log them, and perform a controlled shutdown.

process.on("uncaughtException", (err) => {
    console.log('Uncaught Exception detected! Server shutting down..', err);

    // Close the server if it's running
    if (server) {
        server.close(() => {
            process.exit(1); // Exit with a failure code
        });
    }

    process.exit(1); // Exit immediately if server isn't active
});

// Example of an uncaught exception (you'd typically remove this in production)
// throw new Error("I forgot to handle this local error.");
Enter fullscreen mode Exit fullscreen mode

3. Signal Termination Errors (SIGTERM and SIGINT)

These aren't errors in the traditional sense, but rather signals from the operating system to terminate a process. By listening for these signals, we can ensure our server closes properly, freeing up resources and preventing unexpected behavior.

  • SIGTERM: This is a generic termination signal, often sent by process managers (like PM2 or Kubernetes) to gracefully shut down an application. It gives the application a chance to clean up resources before exiting.

    process.on("SIGTERM", (err) => {
        console.log('Sigterm signal received! Server shutting down..', err);
    
        // Close the server if it's running
        if (server) {
            server.close(() => {
                process.exit(1); // Exit with a failure code
            });
        }
    
        process.exit(1); // Exit immediately if server isn't active
    });
    
  • SIGINT: This signal is typically sent when you press Ctrl+C in your terminal to interrupt a running process.

    process.on("SIGINT", (err) => {
        console.log('Sigint signal received! Server shutting down..', err);
    
        // Close the server if it's running
        if (server) {
            server.close(() => {
                process.exit(1); // Exit with a failure code
            });
        }
    
        process.exit(1); // Exit immediately if server isn't active
    });
    

Implementing these error handling mechanisms helps us build more resilient applications that can withstand unexpected issues and shut down cleanly when needed.

What are your go-to strategies for handling errors in your Node.js applications? Share in the comments! 👇

Top comments (0)