DEV Community

Cover image for Stop Writing try-catch in Every Express Controller — Use This Pattern Instead!
Alifa Ara Heya
Alifa Ara Heya

Posted on

Stop Writing try-catch in Every Express Controller — Use This Pattern Instead!

In Express.js apps, especially when dealing with async operations (like database queries), you've likely repeated the same code structure over and over:

const getAllUsers = async (req, res, next) => {
  try {
    const users = await UserServices.getAllUsers();
    res.status(200).json({ users });
  } catch (error) {
    next(error);
  }
};
Enter fullscreen mode Exit fullscreen mode

✅ This works — but do you really want to write that try...catch block every single time?

Let’s clean it up using a Higher-Order Function.


🚫 The Problem

  • Every controller duplicates the same try-catch-next() logic.
  • It violates the DRY principle (Don't Repeat Yourself).
  • It clutters your code and distracts from the actual business logic.

✅ The Solution: catchAsync

Let’s abstract the error-catching logic into a reusable utility function.

📁 src/utils/catchAsync.ts

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

export const catchAsync = (fn: RequestHandler) => {
  return (req: Request, res: Response, next: NextFunction) => {
    Promise.resolve(fn(req, res, next)).catch((err) => next(err));
  };
};
Enter fullscreen mode Exit fullscreen mode

🧠 What's Going On Here? What Is a Higher-Order Function (HOF)?

The catchAsync function is a Higher-Order Function (HOF).

A Higher-Order Function is simply a function that:

  1. Takes another function as an argument, or
  2. Returns a new function, or
  3. Does both (like catchAsync!)

🔁 Why are HOFs powerful?

  • They allow you to wrap and enhance behavior without repeating code.
  • You can build reusable utilities like authentication guards, validators, logging, and — in our case — async error handlers.

🔍 Deep Dive: How catchAsync Works

const catchAsync = (fn: RequestHandler) => {
  return (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch((err) => next(err));
  };
};
Enter fullscreen mode Exit fullscreen mode

Let’s break this down:

Line Explanation
catchAsync(fn) Takes your controller as an argument
return (req, res, next) Returns a new function with the same signature Express expects
Promise.resolve(...) Ensures even non-async handlers are wrapped in a Promise
.catch((err) => next(err)) Automatically catches any error and forwards it to Express’s global error handler

🧠 So instead of you writing try...catch, catchAsync does it for you!


🎯 Cleaner Controller Code

Now your controller becomes:

import { catchAsync } from '../../../utils/catchAsync';

export const getAllUsers = catchAsync(async (req, res, next) => {
  const users = await UserServices.getAllUsers();
  res.status(200).json({
    success: true,
    message: 'Users retrieved successfully',
    data: users
  });
});
Enter fullscreen mode Exit fullscreen mode

✅ No more try...catch clutter
✅ Just focus on the actual logic


💬 Bonus: Want to Handle Validation or Custom Errors?

You can combine catchAsync with a custom error class like AppError. You can read more about it here (https://dev.to/alifa_ara_heya/building-a-better-apperror-class-in-nodejs-for-robust-api-error-handling-25o)

if (!user) {
  throw new AppError(404, 'User not found');
}
Enter fullscreen mode Exit fullscreen mode

Now your global error handler can cleanly handle all cases.


🧪 TL;DR

Problem Solution
Repeating try/catch in every controller Use catchAsync() to wrap async functions
Messy controllers Cleaner, focused logic
Manual error handling Automatic forwarding to global error middleware

🧠 Final Thoughts

Using Higher-Order Functions like catchAsync makes your codebase:

  • More readable
  • Less error-prone
  • Easier to maintain

It’s a small trick, but it will make your Express codebase much more professional and clean.

🔁 Try it out in your next Express project and say goodbye to repetitive try...catch blocks forever!


#expressjs #javascript #nodejs #backend #cleancode #webdev #devtips

Top comments (0)