Handling Error in express application using typescript
Tags: node, webdev, javascript, programming
Published: 2022-12-04 05:21:02


This is going to be a short blog on handling error in express using typescript.

In this blog I will show how you can **handle error gracefully** in **express**.
Express is one of the most famous *Node.js Frameworks*.


Errors can be divided into two categories in Node.js:
1. Operational errors (e.g. database connection error, invalid user input, etc.)
2. Programming errors (e.g. syntax error, reference error, etc.)



### Handling Programming Errors

Programming errors are usually not handled in Node.js. When they occur Node.js process is terminated. This is because programming errors are usually not recoverable and the process should be terminated to avoid further damage.
but we can listen to `unhandledRejection` and `uncaughtException` events to handle programming errors. But it is recommend to just let the process crash and restart it using a process manager like [pm2](https://www.npmjs.com/package/pm2)

```javascript
// unhandledRejection
process.on('uncaughtException', (err) => {
  // ... log the error ...
  process.exit(1);
});

// un handled promise rejection
process.on('unhandledRejection', (err) => {
  // ... log the error ...
  // server.close will close the server and wait for all the connections to close
  server.close(() => {
    process.exit(1);
  });
});
```

### Handling Operational Errors

Express works with the **concept of middleware**. Middleware is a function that has access to the request and response object. 

Middleware's can

- Modify the request and response object
- End the request-response cycle
- Call the next middleware in the stack

_we also handle error in express using middleware._

let's learn all this by creating a simple express app.

#### Create a simple express app

First, we need to create a new directory and initialize a new node project.

```bash
mkdir express-error-handling
cd express-error-handling

# generates package.json with default settings
npm init -y
```

Now we need to install express and typescript.

```bash
# install dependencies
npm i express typescript ts-node

# install types for express as dev dependency
npm i -D @types/express @types/node

```
we are not going to explore more about configuring typescript but you need  to create a `tsconfig.json` file, you can just run `tsc --init` to generate a default `tsconfig.json` file. We are going to use ts-node to run our typescript code.
next let's create a new file called `index.ts` and add the following code.

```typescript
import express from 'express'

const app = express()

app.get('/', (req, res) => {
    res.send('Hello World')
})

app.listen(3000, () => {
    console.log('Server is running on port 3000')
})
```


now that we have a simple express app, let's add some error handling to it. We are going to create a new file called `error.ts` and add the following code.

```typescript

// base error class
class BaseError extends Error {
    status: number
    isOperational: boolean
    constructor(message: string, status: number, isOperational = true) {
        super(message)
        this.status = status
        this.isOperational = this.isOperational
        Object.setPrototypeOf(this, BaseError.prototype)

    }
}

export {BaseError}

```

This will be the base class to other classes we are going to create to handle other type of errors.
In this error class we have a constructor that takes three arguments.
The first argument is the error message, 
the second argument is the status code and 
the third argument is a boolean value that indicates if the error is operational or not.

Let's add more error classes to our `error.ts` file.


```typescript
// 404 error class
class NotFoundError extends BaseError {
    constructor(message: string) {
        super(message, 404)
        Object.setPrototypeOf(this, NotFoundError.prototype)
    }
}

// validation error class
class ValidationError extends BaseError {
    errorData: Record<string, string>[]
    constructor(data: Record<string,string>[]) {
        super("Validation Error", 400)
        this.errorData = data
        Object.setPrototypeOf(this, ValidationError.prototype)
    }
}
```
we can create as many error classes as we want. In this example I have created two error classes. The first one is the `NotFoundError` class and the second one is the `ValidationError` class.
next we need to create a middleware that will handle the errors.
**Error Handler Middleware** is a middleware that takes four arguments. 
The first argument is the error object, the second argument is the request object, the third argument is the response object and the fourth argument is the next function.
**i.e If you don't provide 4 arguments to a middleware, express will treat it as a normal middleware and not an error handler middleware.**

we now create a new file called `errorHandler.ts` and add the following code.
  
  ```typescript
  // error handler middleware
const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => {
    if(err instanceof ValidationError) {
        return res.status(err.status).json({
            status: 'fail',
            message: err.message,
            data: err.errorData
        })
    } else if (err instanceof BaseError) {
      if( err.isOperational) {
        return res.status(err.status).json({
            status: err.status < 500 && err.status >= 400 ? 'fail' :'error',
            message: err.message
        })
        // 
      } else {
        // log the error
        console.log(err)
        // send generic error message
        return res.status(err.status).json({
            status: 'error',
            message: 'Something went wrong'
        })

      }
    }
}

export {  errorHandler }
```

now that we have created the error handler middleware, we need to add it to our express app. We can do this by adding the middleware using `app.use` method.
let's add the following code to our `index.ts` file.


```typescript
app.use(errorHandler)
```
now we can use our error classes in our controllers and/or middleware to throw errors.
let's add a new route to our `index.ts` file.


```typescript
// check if id is a number
const validateId = (req: Request, res: Response, next: NextFunction) => {
    const id = req.params.id
    if(isNan(id)) {
        next(new ValidationError([
            {
                id: 'id needs to be a number :( '
            }
        ]))
    }
    next()
}

app.get('/users/:id', validateId, (req, res) => {
    const id = req.params.id
    if(id === '1') {
        res.send('User 1')
    } else {
        next(new NotFoundError('User not found'))
    }
})
```

now we can test our app by running the following command.

```bash
npx ts-node index.ts
```
Now when we hit the `/users/2` route, we will get the following response.

```json
{
    "status": "fail",
    "message": "User not found"
}
```
with 404 status code.

and When we hit the route `/users/unknown` we will get the following response.

```json
{
    "status": "fail",
    "message": "Validation Error",
    "data": [
        {
            "id": "id needs to be a number :( "
        }
    ]
}
```

and We have successfully created a simple express app with error handling.

We have used [Jsend](https://github.com/omniti-labs/jsend) to format our API response.
This is it for this blog. I hope you enjoyed it. If you have any questions, feel free to email it :), I am not joking I don't have a comment section on my blog yet.