DEV Community

Cover image for Error Handler
Famosa Adegbite
Famosa Adegbite

Posted on

Error Handler

What is asyncHandler.js?

In a professional Express.js project, asyncHandler.js (or catchAsync.js) is a utility function used to wrap your async route handlers (like signup, login, etc.) so that you don't need to write try...catch blocks in every controller.

Problem It Solves:

Normally in an async controller:

export const signup = async (req, res, next) => {
  try {
    // your logic
  } catch (error) {
    next(error); // manually send error to global error handler
  }
};
Enter fullscreen mode Exit fullscreen mode

That repetition gets messy. asyncHandler makes it cleaner.
The asyncHandler Utility:
You can create a file called utils/asyncHandler.js and write this:

const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

export default asyncHandler;
Enter fullscreen mode Exit fullscreen mode

How to Use It:

Instead of:

export const signup = async (req, res, next) => {
  try {
    // your logic
  } catch (err) {
    next(err);
  }
};
Enter fullscreen mode Exit fullscreen mode

You write:

import asyncHandler from '../utils/asyncHandler.js';

export const signup = asyncHandler(async (req, res, next) => {
  // just write your logic — no need for try-catch
});
Enter fullscreen mode Exit fullscreen mode

Why Using asyncHandler?

  • Cleaner controllers
  • Centralized error handling
  • Less boilerplate
  • Best practice in real-world Express apps

Directory example:

project-root/
├── controllers/
│   └── auth.controller.js
├── middlewares/
│   └── errorHandlers.js
├── utils/
│   └── asyncHandler.js ✅
├── routes/
├── models/
├── app.js
Enter fullscreen mode Exit fullscreen mode

When to use try/catch after using asyncHandler

You still need try/catch inside an asyncHandler only if you are doing something like database transactions or manual rollback — where cleanup is needed even if an error occurs or you need custom logic during failure.

Example: Database transaction rollback

const signup = asyncHandler(async (req, res) => {
  const transaction = await sequelize.transaction();

  try {
    const user = await User.create({...}, { transaction });

    // simulate a bug (e.g., missing env var)
    const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET);

    await transaction.commit();

    res.status(201).json({ token });

  } catch (error) {
    await transaction.rollback(); // 🛑 must rollback manually
    throw error; // let asyncHandler handle the error
  }
});
Enter fullscreen mode Exit fullscreen mode

If you didn’t use try/catch here, the transaction would not rollback, and you'd leave your DB in a broken state (e.g., partial insert).

So:

asyncHandler = automatic error forwarding to Express
try/catch = necessary for manual cleanup logic before the error is forwarded

@adefam

Top comments (0)