Home > Enterprise >  [ExpressJs]: Custom Error handler do not catch exceptions
[ExpressJs]: Custom Error handler do not catch exceptions

Time:05-26

I am creating a router with ExpressJs (using TypeScript) and thrown exceptions are not caught by my custom error handler, here is the code:

In index.ts file (which is the main):

import express, { Express } from 'express';
import cors from 'cors';
import { config } from '~/utils/config';
import { NotFoundRoute } from '~/middlewares/NotFound';
import { ExceptionHandler } from '~/middlewares/ExceptionHandler';
import { usersRouter } from '~/controllers/users';

const app: Express = express();
app.use(express.json());
app.use(cors());
app.use('/users', usersRouter);
app.all('*', NotFoundRoute);
app.use(ExceptionHandler);

app.listen(config.port, () => console.log("API is running on port "   config.port));

The custom exception handler:

import { NextFunction, Request, Response } from 'express';

export const ExceptionHandler = (err: any, req: Request, res: Response, next: NextFunction) => {
  if (res.headersSent)
    return next(err);

  if (err.status && err.message) {
    res.status(err.status).send({ error: err.message });
  } else
    return res.status(500).send({ message: "Internal Server Error" });
}

The router that is problematic (where InternalServerError is not caught by the above):

import { Request, Response, Router } from 'express';
import { User } from '~/schemas/User';
import { InternalServerError } from '~/errors/InternalServerError';

const usersRouter = Router();

usersRouter.get('/', async (req: Request, res: Response) => {
  try {
    const users = await User.find({});
    res.status(200).send(users);
  } catch (err) {
    res.status(500).send({ message: "Failed to fetch users"});
  }
})

usersRouter.post('/register', async (req: Request, res: Response) => {
  try {
    const { email, password } = req.body;
    if (!email || !password)
      res.status(403).send({ message: "Bad Request"});
    const newUser = User.createUser({ email, password });
    await newUser.save();
    res.status(201).send(newUser);
  } catch (err) {
    throw new InternalServerError();
  }
})

export { usersRouter };

All my exception are not caught which means that it is not exception-related. In the index.ts file, the NotFoundRoute throws an exception that is caught, so I guess it works on the file's context. How it is not working ? I suppose the router has a thrown exception that would be caught but it is not.

To remind the context, I am trying to force errors to happen to see if the error handling is correct. So in this case I forced

newUser.save()

to fail.

CodePudding user response:

Express cannot handle thrown exceptions or rejected Promises. To tell express that there is an error you need to call the next() function instead of using throw or rejecting a Promise:

usersRouter.post('/register', async (req: Request, res: Response, next: Function) => {
  try {
    const { email, password } = req.body;
    if (!email || !password)
      res.status(403).send({ message: "Bad Request"});
    const newUser = User.createUser({ email, password });
    await newUser.save();
    res.status(201).send(newUser);
  } catch (err) {

    next(new InternalServerError()); // This is how you "throw" errors
                                     // in Express

  }
})

Note that express only expect you to call the function it passes as the third argument. The name of the argument is up to you. You can use words other than next:

usersRouter.post('/register', async (req: Request, res: Response, error: Function) => {
  try {

    /* some logic */

  } catch (err) {

    error(new InternalServerError());

  }
})

Though traditionally the name of the third argument is next and most javascript developers expect it to be that.

CodePudding user response:

Adding to slebetman's answer: You can throw errors that will be handled by the exception handler, but only in synchronous middleware. You can try it out:

app.use("/sync", function(req, res) {
  throw "sync";
})
.use("/async", async function(req, res) {
  throw "async";
})
.use(function(err, req, res, next) {
  res.end("Caught "   err);
});
  • Related