Express make try/catch block into middleware to avoid duplication

Viewed 264

Im writing my expressJs application, and Im finding in my routes controller the same duplicated code for catching exception, I was wondering how to avoid this.

I have checked this thread, but I get this error "Cannot read property 'catch' of undefined" : 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

as you see in all my routes I have a try catch block with the possible errors (either only a 500, or a 500/404). And I would like to avoid this repetition.

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;

Like I said, it works perfectly now, I would just like to try to avoid the try catch code duplication, and Ive checked the other questions in Stackoverflow but havent helped. The solution ive linked returns a 500 with this catch undefined message (which is not what I want) and on other routes that also have a 404 it just doesnt work.

Thanks a lot!


Update: I followed Heikos advice but still not working

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;
1 Answers

If the code inside your async middleware functions contains an await, you must also wrap it in a try-catch block, otherwise a rejected promise will be unhandled. For example:

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

propagates the error to the error handler, but without the try-catch block it leads to an "UnhandledPromiseRejection".

You can save some typing if you wrap your middleware into a 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 });
}));
Related