skip to content
logo
Table of Contents

Handling Errors in Node.js Applications

A well-structured error-handling strategy improves application stability, simplifies debugging, and enhances maintainability.
This guide covers:

  • Properly handling synchronous and asynchronous errors
  • Using middleware for centralized error handling
  • Logging errors for better debugging
  • Handling uncaught exceptions and rejections

Handling Synchronous Errors

For synchronous code, use try...catch to prevent crashes.

function divide(a: number, b: number): number {
if (b === 0) throw new Error("Division by zero");
return a / b;
}
try {
console.log(divide(10, 0));
} catch (error) {
console.error("Error:", error.message);
}

Handling Asynchronous Errors

Using try...catch inside async functions

async function fetchData() {
try {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
return data;
} catch (error) {
console.error("Failed to fetch data:", error.message);
}
}

Handling Promises with .catch()

fetch("https://api.example.com/data")
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error("Error:", error.message));

Centralized Error Handling in Express

Using a middleware function allows centralized error handling.

import express, { Request, Response, NextFunction } from "express";
const app = express();
app.use((req, res, next) => {
next(new Error("Something went wrong"));
});
app.use((error: Error, req: Request, res: Response, next: NextFunction) => {
console.error("Error:", error.message);
res.status(500).json({ error: error.message });
});

Handling Uncaught Exceptions and Rejections

Uncaught exceptions and unhandled rejections can cause the application to crash. Use global handlers to manage them.

Uncaught Exceptions

process.on("uncaughtException", (error) => {
console.error("Uncaught Exception:", error);
process.exit(1);
});

Unhandled Promise Rejections

process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled Rejection:", reason);
process.exit(1);
});

Logging Errors for Better Debugging

Using a logging library like Winston improves error tracking.

import winston from "winston";
const logger = winston.createLogger({
level: "error",
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: "error.log" }),
new winston.transports.Console(),
],
});
logger.error("Something went wrong");

Conclusion

  • Use try...catch for synchronous and asynchronous errors.
  • Implement centralized error handling in Express.
  • Handle uncaught exceptions and promise rejections globally.
  • Use structured logging for better debugging.

A structured error-handling approach ensures that your application remains stable and debuggable in production.