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
}
};
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;
How to Use It:
Instead of:
export const signup = async (req, res, next) => {
try {
// your logic
} catch (err) {
next(err);
}
};
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
});
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
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
}
});
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
Top comments (0)