CUSTOM NODE JS ERROR HANDLING
Published: 2021-11-09 10:23:09

# Introduction

How we present errors and other response messages to the client determines the ease with which they can handle them. Assume that you are using microservice architecture and different stacks are being used for backend development.
If all the endpoints will be handled using a single frontend client then there is a dire need for uniformity in the way we pass error responses.This will make the frontend developers to come up with a single error handling method.

In this article I will be showing you how to handle and pass error responses to the frontend client using typescript, Node js and express js.

All our error responses will be in the following format:

```json
{
  "errors": [
    {
      "message": "Description",
      "field": "referenced field eg Email"
    },
    {
      "message": "Description ",
      "field": "referenced field eg name"
    }
  ]
}
```

**_Initializing the project_**

- Create a new project directory and open the directory on your terminal.

```bash
mkdir error-handling-example
```

- Change into the newly created directory

```bash
cd error-handling-example
```

- If you have installed node js on your machine, run the following command to initialize the project:

```bash
npm init -y
```

This command will create a package.json file.

- Run the following commands to install the necessary packages:

```bash
npm install -D typescript
npm install -D tslint

```

The -D flag is the shortcut for: --save-dev
The above two commands will install typescript and tslint. tslint will be used to establish coding rules on your project.
Add the following two commands:

```bash
npm install -S express
npm install -D @types/express
npm install -D express-async-errors
npm install -D ts-node-dev
npm install express-validator

```

On your terminal, run the following command to initialize the typescript project configurations:

```bash
tsc --init
```

This will create a tsconfig.json file on your project root directory.
On the root of your project create a new directory src/ and in it a file app.ts

- Open the application on your favourite IDE.

Create the following folders and files inside your src directory to match the structure below

![File structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wj0p6ud3qy3vu26x26wy.png)

## Creating the server

Add the following code to the app.ts file

```ts
import express from "express";
import "express-async-errors";
import { NotFoundError } from "./errors/not-found-err";
import { errorHandler } from "./middlewares/errors-handler";

import authentication from "./routes/routes";

const app = express();
app.use("/api/users", authentication);
app.all("*", async () => {
  throw new NotFoundError();
});
app.use(errorHandler);

const port = 3000;
app.listen(port, () => {
  console.log(`server is listening on ${port}`);
});
```

adds the asynchronous error handling for express js:

```ts
import "express-async-errors";
```

### On the file routes/routes.ts file paste the following piece of code:

```ts
import { signUp } from "../controllers/signup";
import express from "express";
import { body } from "express-validator";

const router = express.Router();

router
  .route("/signup")
  .post(
    [
      body("email").isEmail().withMessage("Invalid email"),
      body("password")
        .trim()
        .isLength({ min: 6, max: 20 })
        .withMessage("Password must be between 6 to 20 characters "),
    ],
    signUp
  );

export default router;
```

As you have noticed this file is imported on the app.ts file as

```ts
import authentication from "./routes/routes";
```

and is uses for route handling on

```ts
app.use("/api/users", authentication);
```

in the app.ts file.

### Handling the controllers

On the controllers/signup.ts file add the following code.

```ts
import { Request, Response } from "express";
import { RequestValidationError } from "../errors/requests-validation-errors";
import { validationResult } from "express-validator";

export const signUp = async (req: Request, res: Response) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    throw new RequestValidationError(errors.array());
  }
  const { email, password } = req.body;
  res.status(201).send({
    email: email,
    password: password,
  });
};
```

On this function you can handle different situations including storigng the user to the database, generating a new token , an encrypted password or validate the imputs further.

### Creating Custom error class

Create an abstract custom error class which extends the Error functions.

- This class will impose a statusCode and an error serializer function to any other class that extends it.

- > NB) Abstract classes can only be extended and not instantiated

The class should look as below

```ts
export abstract class CustomError extends Error {
  abstract statusCode: number;
  constructor(message: string) {
    super(message);
    Object.setPrototypeOf(this, CustomError.prototype);
  }
  abstract serializeErrors(): { message: string; field?: string }[];
}
```

In the same folder errors add the following sample code on the requests-validation.ts file:

```ts
import { ValidationError } from "express-validator";
import { CustomError } from "./custom-error";

export class RequestValidationError extends CustomError {
  statusCode = 400;
  constructor(public errors: ValidationError[]) {
    super("Invalid request params");
    Object.setPrototypeOf(this, RequestValidationError.prototype);
  }
  serializeErrors() {
    return this.errors.map((err) => {
      return { message: err.msg, field: err.param };
    });
  }
}
```

The RequestValidationError class extends the CustomError class created above and therefore must implement a serializeErrors method and statusCode must be instantiated here.

To use the RequestValidationError class on the signup method, add the following code to the middlewares/error-handlers.ts file

```ts
import { Request, Response, NextFunction } from "express";
import { CustomError } from "../errors/custom-error";

export const errorHandler = (
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
) => {
  if (err instanceof CustomError) {
    return res.status(err.statusCode).send({ errors: err.serializeErrors() });
  }
  res.status(400).send({
    errors: [{ message: "Something went wrong" }],
  });
};
```

This part of the signup function can be extracted into a middleware since it will be called many times when a validation is being conducted.

```ts
const errors = validationResult(req);
if (!errors.isEmpty()) {
  throw new RequestValidationError(errors.array());
}
```

If extracted pass it as the second argument on the post method of the route you want to validate i.e:

```ts

.post([validation rules], place-the-extracted-function-here,()=>{})

```

Fire up the terminal and one the project root folder run

```bash

npm start

```

Your project will start on [http://localhost:3000](http://localhost:3000).

You can try out the project using postman:

This is my first article here.
Please don't forget to leave your comment on any improvements that can be done to the article.