Error Handling and Optimization in Express.js

Intro

In this lesson, we'll dive into error handling and optimization for your Express.js applications. These practices are crucial for creating reliable, high-performance web APIs and ensuring a better user experience.

What you'll learn

In this lesson you'll learn:

  • Key concepts and importance of error handling.
  • Implementation of effective error handling in Express.js.
  • Techniques to optimize your application performance.
  • Creating and improving error messages.

Step 1: Understand Error Handling

Understanding error handling helps enhance user experience and aids in debugging by managing unexpected issues in your application. Issues might include invalid user inputs, server errors, or resource not found scenarios.

Consider the following code snippet for simple error handling in a GET /users/:id route:

app.get('/users/:id', (req, res) => {
    const user = users.find(user => user.id === parseInt(req.params.id));
    if (!user) return res.status(404).send('User not found');
    res.json(user);
});

This code attempts to find a user by their ID. If the user isn't found, a 404 status code is returned with the message 'User not found'. Handling this scenario ensures that clients receive a clear message instead of a generic error.

Step 2: Implement Centralized Error Handling with Middleware

Centralized error handling in Express.js allows managing errors in one place, improving code maintainability and ensuring consistent error responses.

Centralized error handling middleware example:

app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send('An unexpected error occurred. Please try again later');
});

This middleware function catches any errors that occur in the routes and logs the error stack for debugging purposes. Usually, 500 error messages, which indicating an internal server error, are not displayed for security reasons; only a generic message is shown on the client side.

To expand, you might create custom error classes and handle different error types accordingly:

class NotFoundError extends Error {
    constructor(message) {
        super(message);
        this.name = 'NotFoundError';
        this.statusCode = 404;
    }
}

app.use((err, req, res, next) => {
    console.error(err.stack);
    const statusCode = err.statusCode || 500;
    res.status(statusCode).send(err.message);
});

The above code snippet defines a custom NotFoundError class that extends the built-in Error class, setting its name to 'NotFoundError' and its status code to 404. You can throw a new NotFoundError('User not found') within a route if a user isn't found. In the app.use middleware block, the thrown error is caught, logged with its stack trace, and the corresponding status code is sent back to the client. If the custom error class is used, its predefined status code (404 in this case) will be returned; otherwise, a default 500 status code is used. This ensures consistent and meaningful error responses throughout the application.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal