Transform Try/Catch Block into Middleware in Express to Prevent Redundancy

Is it possible for node.js or Express to recognize an error in my route, produce an Error object, and pass it to my middleware using the wrapAsync wrapper function? By wrapping things in a function, it’s probable that the exception (and its corresponding Error object) will be visible in your handler.

Question:

As I develop my ExpressJS application, I’ve noticed that my routes controller contains repetitive code for handling exceptions. I’m curious to know how I can eliminate this redundancy.

Upon reviewing this thread, an error with the following code was encountered:

"Cannot read property 'catch' of undefined"

, which produced the following result:
Express Try and Catch in Form of middleware
.

this is my route.js

const express = require("express");
const createHttpError = require("http-errors");
const Validator = require("../middlewares/Validator");
const TaskNotFoundException = require("../services/TaskNotFoundException");
const TaskService = require("../services/TaskService");
router.get("/tasks", async (req, res, next) => {
  try {
    const data = await TaskService.getTasks();
    res.send({ code: 200, message: "Success", data });
  } catch (error) {
    next(createHttpError(500));
  }
});
router.get("/task/:id", async (req, res, next) => {
  const { id } = req.params;
  try {
    const data = await TaskService.getTask(id);
    res.send({ code: 200, message: "Success", data });
  } catch (error) {
    if (error instanceof TaskNotFoundException) {
      next(createHttpError(404));
    } else {
      next(createHttpError(500));
    }
  }
});
and the list goes on

In all of my routes, I have attempted
catch block
and taken into account any potential errors, which may include a 500 or a 500/404. I aim to eliminate this recurring pattern.

this is my app.js

const express = require("express");
const bodyParser = require("body-parser");
const createHttpError = require("http-errors");
const api = require("./routes/api");
const app = express();
app.use(express.json());
app.use(bodyParser.json());
app.use("/api", api);
// Catch HTTP 404
app.use((req, res, next) => {
  next(createHttpError(404));
});
// Error Handler
app.use((err, req, res, next) => {
  res.status(err.status || 500);
  res.json({
    error: {
      status: err.status || 500,
      message: err.message,
    },
  });
});
module.exports = app;

As previously mentioned, the code is currently functioning without issues. However, I am seeking a way to avoid duplicating the try catch code. Despite reviewing related queries on Stackoverflow, I have not found a suitable solution. When attempting the suggested solution, it returns a 500 error with an undefined message, which is not desirable. In addition, the code fails to work on other routes that also have a 404 error.

Thanks a lot!


Despite following Heiko’s advice, the issue still persists.

api.js

const express = require("express");
const createHttpError = require("http-errors");
const Validator = require("../middlewares/Validator");
const TaskNotFoundException = require("../services/TaskNotFoundException");
const TaskService = require("../services/TaskService");
const router = express.Router();
router.get("/tasks", async (req, res, next) => {
  const data = await TaskService.getTasks();
  res.send({ code: 200, message: "Success", data });
});

app.js

const express = require("express");
const bodyParser = require("body-parser");
const createHttpError = require("http-errors");
const api = require("./routes/api");
const app = express();
app.use(express.json());
app.use(bodyParser.json());
app.use("/api", api);
function catchAsyncErrors(middleware) {
  return async function(req, res, next) {
    try {
      await middleware(req, res, next);
    } catch(err) {
      next(err);
    }
  };
}
// Catch HTTP 404
app.use(catchAsyncErrors((req, res, next) => {
  next(createHttpError(404));
}));
// Error Handler
app.use(catchAsyncErrors((err, req, res, next) => {
  res.status(err.status || 500);
  res.json({
    error: {
      status: err.status || 500,
      message: err.message,
    },
  });
}));
module.exports = app;




Solution:

In case there is an

await

within your async middleware functions, it is necessary to enclose it with a

try-catch

block. Otherwise, an unhandled
rejected promise
may occur. A practical instance is illustrated below:

app.use(async function(req, res, next) {
  try {
    await Promise.reject("error");
  } catch(err) {
    next(err);
  }
});

Without the inclusion of the

try-catch

block, an “UnhandledPromiseRejection” error occurs as the error is passed on to the error handler.

To reduce the amount of typing required, you can encapsulate your middleware in a designated

catchAsyncErrors

function.

function catchAsyncErrors(middleware) {
  return async function(req, res, next) {
    try {
      await middleware(req, res, next);
    } catch(err) {
      next(err);
    }
  };
}
router.get("/tasks", catchAsyncErrors(async (req, res, next) => {
  const data = await TaskService.getTasks();
  res.send({ code: 200, message: "Success", data });
}));

Frequently Asked Questions