Express JS Error Handling Tutorial with Best Examples

By Digamber Rawat Last updated on
In this Express JS tutorial, we’ll briefly go over what express js is, how to install it and the most importantly how to handle errors in an Express app. Express.js allows us to catch and process both synchronous and asynchronous errors efficiently for any unexpected situation. Express offers middleware functions to deal with errors.

Introduction Express Js

Express.js is a web application framework based on Node.js. The core philosophy of Express is based on Http Modules and connect components, which are known as middleware. Express is designed to build a robust web application and RESTful APIs. Express.js provides a high level of flexibility to web developers when it comes to building the latest web applications.

Setting Up Express JS Project with Node

Run command in terminal to create a basic Node and Express js project from scratch:

mkdir expressjs-error-handling

Enter into the project folder:

cd expressjs-error-handling

Open the project folder in the code editor.

Run the below command to create the package.json file for Express.JS error handling project.

npm init -y

Following will be the output displayed on your terminal:

{
  "name": "expressjs-error-handling",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Setting up Express Server

In this step, we set up express js server, but before that install Express js package by running the given below command:

npm install express

Now, create an app.js file in the project’s root folder. In this file, we are about to keep our app’s basic configurations.

touch app.js

Then, add the Express server configurations:

// app.js

const express = require('express'),
  PORT = process.env.PORT || 4000;

// Express Configuration
const app = express();

// Set up PORT
app.listen(PORT, () => {
  console.log('Connected to port ' + PORT)
})

Your node server is ready to be served, save your app.js file and run the following command.

node app
# node app
# Server is running at PORT: 4000

You can check out your basic express app on the following url: http://localhost:4000

Basic Express Error Handling Example

A web application is made of various services and databases. When a web application receives the data, that data is processed through various services in the backend. While the process is going on, any unexpected thing may occur. A database or service might get failed. A web application should be ready to handle such kind of situation, and this is where the error handlers come into the limelight.

Let us check out the basic error handling example in Express.

// Express middleware
app.get('/employees', async function(req, res) {
  let employees;
  try {

    employees = await db.collection('Employees').find().toArray();

  } catch (error) {

    // User friendly error response
    res.status(500).json({ 
      error: error.toString() 
    });
  }

  res.json({ employees });
});

To give you the demo of how does error handling work in Express. We declared the '/employees' endpoint. When users access '/employees' endpoint, then the try-catch block will be triggered. And, If any error shows up, then, it will throw the exception; otherwise, the employee’s data will be returned. This is just a brief error handling example for handling one or two endpoints in an Express middleware.

Define Error Handling in Express App

Now, we’ll learn to integrate error handling using Express functions which are known as middleware. A req, res, and next arguments are denoted by middleware in Express. In the below example, we’ll see how to use middleware to handle the errors.

app.use((err, req, res, next) => {
  res.status(500).send('Error found');
});

Express Wildcard 404 Error Handling

We can also define error handling for wildcard routes using '*' in our routes if a user tries to access the route which doesn’t exist in our Express app. Express will throw the error or in simple terms will be triggered a 404 error in this case.

app.get('*', function(req, res, next) {
  let err = new Error("Page Doesn't Exist");
  err.statusCode = 404;
  next(err);
});

However, we can make it better, by telling Express that a user should receive a 404 error and also must be redirected to the custom error page.

app.use(function(err, req, res, next) {
  console.error(err.message);
  
  // Define a common server error status code if none is part of the err.
  if (!err.statusCode) err.statusCode = 500; 

  if (err.shouldRedirect) {
    // Gets a customErrorPage.html.
    res.render('customErrorPage')

  } else {
    // Gets the original err data, If shouldRedirect is not declared in the error.
    res.status(err.statusCode).send(err.message);
  }
});

Not to Be Followed Error Handling Approach in Express

If we throw the errors asynchronously in the same function, then It’ll inevitably crash the server every time an HTTP request is made. Due to the JavaScript’s default asynchronous nature.

// app.js

const express = require('express'),
  PORT = process.env.PORT || 4000;

// Express settings
const app = express();

// Express Error Handling
app.get('*', (req, res, next) => {
  setImmediate(() => {
    throw new Error('Something is really not good!');
  });
});

app.use((error, req, res, next) => {
  // This error can't be displayed
  res.json({
    message: error.message
  });
});

// Set up PORT
app.listen(PORT, () => {
  console.log('Connected to port ' + PORT)
})

Best Approach For Error Handling in Express

Now, I’ll show you the best approach to handle errors in Express js. The next middleware is helpful when it comes to defining errors accurately in Express. This argument is passed in the route handler, as mentioned below.

// app.js

const express = require('express'),
  PORT = process.env.PORT || 4000;

// Express settings
const app = express();

// Express Error Handling
app.get('*', (req, res, next) => {
  // Error goes via `next()` method
  setImmediate(() => {
    next(new Error('Something went wrong'));
  });
});

app.use((error, req, res, next) => {
  // Error gets here
  res.json({
    message: error.message
  });
});

// Set up PORT
app.listen(PORT, () => {
  console.log('Connected to port ' + PORT)
})

The vital point to be considered here is that the Express js middlewares execute in an orderly manner, we must define the error handlers after all the other middleware functions. If you don’t follow this process, then your handlers won’t work as expected.

Using Error Handling with Async/Await

Most of the Express js was created before EcmaScript 6 in between year 2011 till 2014. I know this is not the best approach to handle errors with async/await. However, error handling can also be made possible with JavaScript’s async/await approach with the help of a custom asyncHelper middleware function.

// app.js

const express = require('express'),
  PORT = process.env.PORT || 4000;

// Express settings
const app = express();

// Error Handling Helper Function
function asyncHelper(fn) {
  return function (req, res, next) {
    fn(req, res, next).catch(next);
  };
}

// Express Error Handling async/await
app.get('*', asyncHelper(async (req, res) => {
  await new Promise(resolve => setTimeout(() => resolve(), 40));
  throw new Error('Error found');
}));

app.use((error, req, res, next) => {
  res.json({
    message: error.message
  });
});

// Set up PORT
app.listen(PORT, () => {
  console.log('Connected to port ' + PORT)
})

As you can see in the example, we bind a custom helper function asyncHelper with async/await. It is playing a vital role in Express error handling as a middleware. The async function returns a promise, we used the .catch() method to get the errors and pass it to the next() method.

Conclusion

Finally, we have completed the Express error handling tutorial with real-world examples. I hope you liked the examples I mentioned in this tutorial. I have talked about every possible situation of error-handling you might face while developing a real-world express application. However, if I have skipped anything do let me know. It will help us to enhance my knowledge as well. Thanks for reading, have a good day.

Digamber Rawat
Digamber Rawat

Full stack developer with a passion for UI/UX design. I create beautiful and useful digital products to solve people’s problem and make their life easy.