Express.js Server Error Handling Explained
Tags: express, javascript, beginners, node
Published: 2023-05-26 14:23:05

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](#chapter-1)
        [Building Components for Efficiently Dealing With Errors](#chapter-2)
        [Endpoint Creation](#chapter-3)
        [Web Server Example Errors](#chapter-4)

Let's dive in 🦅.

## Setting up Express Server <a name="chapter-1"></a>

**Creating the project**
```
mkdir error-handling
cd error-handling
npm init
npm i express nodemon
```

- 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}`)
});
```
- 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"
  }
}
```
- 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m2mdcqcqfcxxtcc8hnc6.png)
Hurray we have our server running 🎉 

--------------------------------------------------------------
## Building Components for Efficiently Dealing With Errors <a name="chapter-2"></a>

**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;

```

**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;
```

**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);
  }
}
```
--------------------------------------------------------------

## Endpoint Creation <a name="chapter-3"></a>

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}`)
});
```
`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;
```
--------------------------------------------------------------
## Web Server Example Errors <a name="chapter-4"></a>

**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');
});
```
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;
```

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

```
(routes/user.route.js)

router.get('/user', authenticated, userController);
```
If you query the `/user` endpoint in Postman without authorization, you will receive a 401 error, as seen in the picture below.


![post-man](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cyxb0zr257lzawp6zbh4.png)

**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);
});
```
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](http://expressjs.com/en/guide/error-handling.html#error-handling) 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](http://expressjs.com/en/guide/error-handling.html#error-handling)

2. [Github Repo](https://github.com/daveOnactive/error-handling)