DEV Community

Cover image for Express.js Server Error Handling Explained
Chidiebere
Chidiebere

Posted on

Express.js Server Error Handling Explained

Effective error handling on the web server is a critical component of developing a resilient system, since when an error is handled effectively, you are less likely to become irritated when anything goes wrong. By supplying helpful information and precise error code, you will be able to troubleshoot quickly.
Only operational errors will be addressed in this article. Before we go into how to handle operational failures, let's build a node.js server using express.js. Express.js is built on top of node.js, a powerful JavaScript engine that enables server-side programming.

Table Of Contents
Setting up Express Server
Building Components for Efficiently Dealing With Errors
Endpoint Creation
Web Server Example Errors

Let's dive in 🦅.

Setting up Express Server

Creating the project

mkdir error-handling
cd error-handling
npm init
npm i express nodemon
Enter fullscreen mode Exit fullscreen mode
  • Use mkdir to create a folder in the root directory and move (cd) to the folder that was created.
  • Create a package.json that will keep record of all the dependencies that will be used for this article.
  • Install the following dependencies.
  • nodemon to listen for changes in the source file and restart the server

Running the server

  • create an app.js file
(app.js)
const app = express();
const PORT = 3000;

app.get('/', (req, res) => {
  res.send('Welcome to error handling in Express :)');
});

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`)
});
Enter fullscreen mode Exit fullscreen mode
  • In app.js create an instance of express
  • Create the server and listen to incoming HTTP request on port 3000.
  • Define the root route that handles HTTP GET request
{
  "name": "error-handling",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "nodemon app.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.18.2",
    "joi": "^17.9.2",
    "nodemon": "^2.0.22"
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Add a start script in the package.json file, the start script will start the server using nodemon.
  • On postman run localhost:3000/

server post-man
Hurray we have our server running 🎉


Building Components for Efficiently Dealing With Errors

Error handler middleware: Any code that runs between the request and the answer is referred to as middleware. The error handler middleware in this scenario will be an abstraction of the many errors that will be handled in the server.

  • It accepts error, request, response and next parameters.
  • If the error that is passed is an instance of the custom AppError, handleError function respond with the statusCode, errorMessage and errorStatus.
  • If the error is not an instance of AppError the respond will be a 500 errorCode and an internal server error message, because the error that occurred wasn't taken into consideration.
(middleware/handlError.js)

const AppError = require('../utils/AppError');

const handleError = (err, req, res, next) => {

  if (err instanceof AppError) {
    return res.status(err.statusCode).json({ 
      message: err.message, 
      status: err.status 
    });
  }

  res.status(500).json({ 
    message: 'Internal Server Error', 
    status: 500 
  });
}

module.exports = handleError;

Enter fullscreen mode Exit fullscreen mode

AppError Class: A customized error called AppError extends the new Error(..) base error class.

  • It accepts the message argument and the errorCode.
  • Super method runs the base error constructor with the message parameter.
(utils/AppError.js)

class AppError extends Error {
  constructor(statusCode, message) {
    super(message);

    this.statusCode = statusCode;
    this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
  }
}

module.exports = AppError;
Enter fullscreen mode Exit fullscreen mode

TryCatch Abstraction: An abstraction called tryCatch delivers a middleware with a try-and-catch block when it receives a controller as an argument.

  • The controller is called in the try block, and If an error occurs the next parameter is called in the catch block with the specified error.
exports.tryCatch = (controller) => async (req, res, next) => {
  try {
    await controller(req, res, next);
  } catch(err) {
    next(err);
  }
}
Enter fullscreen mode Exit fullscreen mode

Endpoint Creation

Before we build a model endpoint that will be utilized to show how errors are effectively handled. Let's add handleError middleware to the server's index file (app.js).

const express = require('express');
const handleError = require('./middleware/handleError');

const app = express();
const PORT = 3000;

app.get('/', (req, res) => {
  res.send('Welcome to error handling in Express :)');
});

// error handler middleware
app.use(handleError);

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`)
});
Enter fullscreen mode Exit fullscreen mode

handleError middleware will be used to handle errors that occurs during a HTTP process.

Creating an Example Express Router

  • create a user endpoint that returns an array of users. This endpoint will be used to experiment several operational error that can be handled in a HTTP process.
(routes/user.route.js)

const express = require('express');
const { tryCatch } = require('../utils/tryCatch');
const { getUsers } = require('../service/users.service');

const router = express.Router();

const userController = tryCatch(async (req, res, next) => {
  const users = await getUsers();
  res.status(200).json(users);
});

router.get('/user', userController);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Web Server Example Errors

Bad Request Error, often known as 400: When the web server cannot handle the request given by the web client, a 400 error is one form of HTTP response status that appears. This kind of error is likely caused by the web client. There are several things that might result in a 400 error.

  • When the endpoint URL contains a mistake
  • When the web client gives the web server inaccurate or incomplete information.
  • A browser or internet connection issue for the user

code example

We can handle an invalid endpoint error in the app.js file, which is an example of a bad request error.

(app.js)
app.get('*', (req, res) => {
  throw new AppError(400, 'Invalid endpoint');
});
Enter fullscreen mode Exit fullscreen mode

The asterisk character in Express Router can be used to match any number of characters in a route. The asterisk route, for example, will be triggered when the specified URL does not match any of the existing routes. In that instance, the web server returns a 400 error code.

Unauthorized Error, often known as 401: When a web client communicates with a web server without the necessary rights, the web server responds with a 401 error, indicating that the user is not authorized to do the intended activity.

code example

To demonstrate the 401 error, let's build a user authentication middleware that checks to see if the user is authorized before completing the user request.

const AppError = require('../utils/AppError');

const authenticated = (req, res, next) => {
  const isAuthenticated = req.headers.authorization;

  if (!isAuthenticated) {
    throw new AppError(401, 'unauthorized request');
  }

  next();
}

module.exports = authenticated;
Enter fullscreen mode Exit fullscreen mode

The authorized middleware will then be used in the user route.

(routes/user.route.js)

router.get('/user', authenticated, userController);
Enter fullscreen mode Exit fullscreen mode

If you query the /user endpoint in Postman without authorization, you will receive a 401 error, as seen in the picture below.

post-man

Not Found Error, often known as 404: A 404 HTTP response status occurs when the web client can interact with the web server but the web server is unable to locate the requested resource. Consider the following scenario: you attempt to fetch your t-shirt from your closet and you can't find it, but you can go into your closet. I hope this was helpful XD.

code example

Assume there are no users on the server at the moment. When we request the users, the server returns a 404 error. The modifications may be done on the user controller.

const userController = tryCatch(async (req, res, next) => {
  const users = undefined;
  if (!users) {
    throw new AppError(404, 'No users found')
  }
  res.status(200).json(users);
});
Enter fullscreen mode Exit fullscreen mode

Run the /user endpoint in your postman.

Internal Server error, often known as 500: A 500 response status indicates a web server issue and has nothing to do with the web client. The following factors may contribute to the occurrence of a 500 error:

  • When a problem arises with the web server hosting provider
  • The reason for a failed database connection
  • A programming error occurred in the web server code.

Conclusion

Finally, it is critical to always have a correct error-handling structure on your web server in order to maximize the possibilities of troubleshooting specific operational errors that may occur when working on the web server. I recommend you read this to learn more about how to handle errors efficiently in an express server.
Remember that your education does not end here. Thank you for sticking with me to the finish.


Resources

  1. Express.js Documentation

  2. Github Repo

Top comments (1)

Collapse
 
josethz00 profile image
José Thomaz

very good, thanks!