Paramclasses Academy - Blog

Tutorials Details

←
Post 14 of 40
→

Node.js Error Handling: Best Practices for Reliable Applications

Node.js Error Handling: Best Practices for Reliable Applications

October 29, 2026

✦ Node.js Error Handling: Best Practices for Reliable Applications

❖ Introduction

Errors are a normal part of software development. A file may not exist, a database connection may fail, an API request may return an unexpected response, or a user may submit invalid information. If these situations are not handled properly, a Node.js application can behave unexpectedly or fail.

Error handling in Node.js helps developers detect errors, understand what went wrong, and respond safely without exposing sensitive information.

Whether you are developing a REST API, a student management system, an e-commerce website, or a backend application, understanding error handling is essential.

In this tutorial, we will learn about common types of errors, try...catch, asynchronous error handling, Express.js error middleware, custom errors, and practical best practices for reliable Node.js applications.

✦ What Is Error Handling in Node.js?

Error handling is the process of identifying, managing, and responding to problems that occur while a program runs.

For example, consider an application that reads a file:

const fs = require("fs");

const data = fs.readFileSync("student.txt", "utf8");

console.log(data);

If student.txt does not exist, Node.js throws an error. Without proper handling, the program may terminate.

A safer version is:

const fs = require("fs");

try {
    const data = fs.readFileSync("student.txt", "utf8");
    console.log(data);
} catch (error) {
    console.error("Unable to read the file:", error.message);
}

Now the application catches the error and displays a useful message.

❖ Why Is Error Handling Important?

Good error handling provides several benefits:

  • Application stability: Expected failures can be handled gracefully.

  • Better debugging: Error messages and logs help developers identify problems.

  • Security: Internal paths, database details, and stack traces can be kept out of public responses.

  • Better user experience: Users receive understandable messages instead of confusing technical errors.

  • Easier maintenance: Consistent error handling makes applications easier to test and update.

✦ Common Types of Errors in Node.js

❖ 1. Syntax Errors

Syntax errors occur when JavaScript code does not follow the language's syntax rules.

console.log("Hello World"

The closing parenthesis is missing, so the code cannot be parsed correctly.

❖ 2. Runtime Errors

Runtime errors occur while the program is executing.

const student = undefined;

console.log(student.name);

Accessing name on undefined causes a TypeError.

❖ 3. Logical Errors

Logical errors occur when the code runs but produces an incorrect result.

const price = 100;
const quantity = 3;

const total = price + quantity;

console.log(total);

The code prints 103, but if the intention is to calculate the total cost, multiplication is required:

const total = price * quantity;
console.log(total); // 300

❖ 4. Asynchronous Errors

Asynchronous errors can occur during operations such as file access, network requests, or database queries. These require appropriate handling for the API being used.

For example, Promise-based file operations can be handled using try...catch with await.

✦ Using try...catch in Node.js

The try...catch statement lets you catch exceptions thrown while executing code in the try block.

Example:

try {
    const result = 10 / 0;

    if (!Number.isFinite(result)) {
        throw new Error("The result is not finite");
    }

    console.log(result);
} catch (error) {
    console.error("Error:", error.message);
}

Output:

Error: The result is not finite

JavaScript does not throw an error for division by zero in this example. Instead, it produces Infinity, so the explicit validation and throw statement create an error for us.

The catch block receives the error object, which commonly contains a message and may include a stack for debugging.

✦ Handling Errors with Async/Await

When working with asynchronous functions that return Promises, use try...catch around await expressions.

Example: Reading a file asynchronously

const fs = require("fs/promises");

async function readStudentFile() {
    try {
        const data = await fs.readFile("student.txt", "utf8");

        console.log("Student information:");
        console.log(data);
    } catch (error) {
        console.error("Unable to read student file:", error.message);
    }
}

readStudentFile();

If the file is missing or cannot be accessed, the Promise rejects and the catch block handles the error.

Important: A try...catch block only catches errors that occur within its synchronous execution or asynchronous operations that are properly awaited inside it. A Promise started but not awaited may reject outside that block.

✦ Handling Promise Errors with .catch()

You can also handle Promise rejections using .catch().

const fs = require("fs/promises");

fs.readFile("student.txt", "utf8")
    .then((data) => {
        console.log(data);
    })
    .catch((error) => {
        console.error("File operation failed:", error.message);
    });

This approach is useful when working with Promise chains. Choose .catch() or try...catch with async/await based on which makes your code clearer.

✦ Creating Custom Errors in Node.js

Built-in errors are useful, but custom errors can make application logic easier to understand.

For example, a student management application might need to report when a requested student does not exist.

class StudentNotFoundError extends Error {
    constructor(message) {
        super(message);
        this.name = "StudentNotFoundError";
    }
}

function getStudent(id) {
    const students = [
        { id: 1, name: "Rahul" },
        { id: 2, name: "Priya" }
    ];

    const student = students.find((item) => item.id === id);

    if (!student) {
        throw new StudentNotFoundError("Student not found");
    }

    return student;
}

try {
    console.log(getStudent(5));
} catch (error) {
    if (error instanceof StudentNotFoundError) {
        console.error(error.message);
    } else {
        console.error("An unexpected error occurred");
    }
}

This example defines a dedicated error type. As the application grows, custom errors can help distinguish expected business conditions from unexpected failures.

✦ Error Handling in Express.js

Express.js is a popular framework for building Node.js web applications and REST APIs.

Express supports centralized error-handling middleware. This lets you handle errors in one place instead of repeating response logic in every route.

❖ Step 1: Install Express

Create a project folder and run:

npm init -y
npm install express

❖ Step 2: Create server.js

const express = require("express");

const app = express();
app.use(express.json());

app.get("/student", (req, res, next) => {
    try {
        const student = null;

        if (!student) {
            const error = new Error("Student not found");
            error.status = 404;

            throw error;
        }

        res.json(student);
    } catch (error) {
        next(error);
    }
});

// Error-handling middleware
app.use((err, req, res, next) => {
    console.error(err.stack);

    const statusCode = err.status || 500;

    res.status(statusCode).json({
        success: false,
        message: statusCode === 500
            ? "Internal server error"
            : err.message
    });
});

app.listen(3000, () => {
    console.log("Server running at http://localhost:3000");
});

❖ Step 3: Run the Application

node server.js

Open http://localhost:3000/student in your browser or an API client.

The route produces a 404 response because the example deliberately sets the student to null.

The error middleware receives the error through next(error) and sends a JSON response.

Note: Express 5 automatically forwards rejected Promises from returned async route handlers to error middleware. In Express 4, asynchronous errors generally need to be forwarded explicitly, for example with next(error) or an appropriate wrapper.

✦ Best Practices for Node.js Error Handling

❖ 1. Use Meaningful Error Messages

Write messages that help developers understand the problem. Avoid vague messages such as "Something went wrong" in internal logs when more useful context is available.

❖ 2. Avoid Exposing Sensitive Details

Do not send database credentials, internal file paths, SQL statements, stack traces, or other sensitive implementation details to public API clients.

Return safe messages to users and keep detailed diagnostic information in protected logs.

❖ 3. Use Appropriate HTTP Status Codes

Common status codes include:

  • 400 Bad Request — invalid request data.

  • 401 Unauthorized — authentication is required or invalid.

  • 403 Forbidden — the user does not have permission.

  • 404 Not Found — the requested resource does not exist.

  • 500 Internal Server Error — an unexpected server-side failure occurred.

Select the status code based on the actual problem.

❖ 4. Validate User Input

Never assume that user-provided data is correct. Validate request bodies, query parameters, and route parameters before using them.

❖ 5. Log Errors Properly

Use structured logging in production applications. Include useful context such as the request ID, route, and error type while avoiding passwords, tokens, and other sensitive data.

❖ 6. Do Not Ignore Promise Rejections

Handle expected Promise failures with await and try...catch or .catch(). Unhandled rejections should not be treated as a substitute for proper application-level error handling.

❖ 7. Keep Error Handling Consistent

A shared error-handling approach makes API responses predictable and helps developers maintain the application.

✦ Frequently Asked Questions (FAQs)

1. What is error handling in Node.js?

Error handling is the process of detecting and managing errors so an application can respond safely and provide useful diagnostic information.

2. What is try...catch in Node.js?

It is a JavaScript statement used to catch exceptions thrown within the try block.

3. How do I handle errors in async functions?

Use try...catch around awaited Promise operations, or handle the returned Promise with .catch().

4. What is Express error-handling middleware?

It is middleware with four parameters—err, req, res, and next—that centralizes error handling for an Express application.

5. Should I show stack traces to users?

No. Detailed stack traces should generally be restricted to protected logs or development environments, not public production responses.

6. What is a custom error?

A custom error is an error class created for a specific application condition, such as a missing student or invalid order.

✦ Conclusion

Error handling is essential for building reliable Node.js applications. By using try...catch, handling Promise rejections, creating custom errors, validating user input, and setting up Express error middleware, developers can make applications easier to debug and safer to operate.

Practise the examples in this tutorial and apply these techniques to your own REST APIs and database-driven projects.

Keep learning and keep building with Param Classes!

←
Post 14 of 40
→