DEV Community

Cover image for Are You Bored of Try Catch😒
Nurul Islam Rimon
Nurul Islam Rimon

Posted on

Are You Bored of Try Catch😒

Handling Errors in Express with a Custom catchAsync Middleware
When building web applications using Express, error handling is one of the most crucial aspects to ensure smooth operations and user experience. Improper error handling can result in unexpected crashes, poor user feedback, and general frustration for both developers and users. A popular approach in modern JavaScript applications is to use asynchronous functions, and this is where handling errors properly becomes even more important.

This CatchAsync function will help you to optimize your code and organize it:

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

export const catchAsync = (fn: RequestHandler) => {
    return async (req: Request, res: Response, next: NextFunction) => {
        try {
            await fn(req, res, next);
        } catch (error) {
            next(error);
        }
    };
};

Enter fullscreen mode Exit fullscreen mode

How It Works

  1. Takes an Asynchronous Function: The catchAsync function receives an asynchronous route handler (fn), which is a function that processes incoming requests.

  2. Executes the Function: The utility wraps the provided function and ensures it is executed asynchronously. If the function resolves correctly, it proceeds as usual.

  3. Catches Errors: If an error occurs while executing the asynchronous function (for example, during a database operation), the catchAsync middleware catches the error.

  4. Passes Errors to the Next Middleware: Any caught errors are passed to the next middleware using next(error), allowing you to handle errors globally with a centralized error handler.

Using catchAsync in Your Application

Here’s how you can use the catchAsync utility in your route handlers:

import express from "express";
import { catchAsync } from "./catchAsync"; // Importing our custom utility

const app = express();

// Sample asynchronous route handler
const getUserData = async (req: Request, res: Response, next: NextFunction) => {
    const userData = await fetchUserDataFromDatabase(req.params.id); // Assume async DB call
    res.json(userData);
};

// Use the catchAsync utility to wrap your async route handler
app.get('/user/:id', catchAsync(getUserData));

// Global error handler
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
    console.error(err);
    res.status(500).send({ error: err.message });
});

app.listen(3000, () => {
    console.log("Server is running on http://localhost:3000");
});

Enter fullscreen mode Exit fullscreen mode

Benefits of catchAsync

  • Cleaner Code: By using catchAsync, you avoid repetitive try-catch blocks in every route handler, making the code more readable and maintainable.

  • Centralized Error Handling: With this utility, all errors (both synchronous and asynchronous) are caught and passed to a central error handler, improving the overall structure of your application.

  • Enhanced Debugging: Since errors are consistently forwarded to the error-handling middleware, debugging becomes easier. All errors are handled in one place, and you don’t need to worry about missing a specific error.

Best Regards,
N I Rimon🌹

Top comments (11)

Collapse
 
m__mdy__m profile image
genix

The approach in this article is great, but it’s not always the best solution. While wrapping async functions with catchAsync removes repetitive try-catch blocks, it still ties error handling to Express’s middleware flow. This means that if you need more control over error propagation, custom error reactions, or want to handle errors outside the typical HTTP request cycle, this pattern can feel limiting.

In contrast, with Gland you can leverage its event-driven nature to decouple error handling from the core logic. Instead of just using try-catch, you could emit an error event like so:

try {
  // some code
} catch (e) {
  ctx.emit("handler:error", ctx);
}
Enter fullscreen mode Exit fullscreen mode

This gives you the flexibility to manage errors in a more modular way, which can be especially useful in complex systems. Overall, while the catchAsync pattern works well for many Express applications, using an event-based approach like in Gland can offer a more adaptable solution depending on your project’s needs.

Collapse
 
nurulislamrimon profile image
Nurul Islam Rimon • Edited

Thanks, Genix💚
It will be very helpful for me.

Collapse
 
vitalets profile image
Vitaliy Potapov

The article makes a great point! For a more general approach, there's a try operator proposal for JavaScript that will hopefully reduce the need to write try-catch blocks as often.

Collapse
 
nurulislamrimon profile image
Nurul Islam Rimon

Thank you Vitaliy Potapov❤️

Collapse
 
gamelord2011 profile image
Reid Burton

I want the tc39 for the const[error, resp] ?= await(response); format to go through.

Collapse
 
larastewart_engdev profile image
Lara Stewart - DevOps Cloud Engineer

I'm also waiting on this. 🫤

Collapse
 
nurulislamrimon profile image
Nurul Islam Rimon

Thank Reid Burton!

Collapse
 
syedmuhammadaliraza profile image
Syed Muhammad Ali Raza

article content is unique but mostly dev's used try catch

Collapse
 
nurulislamrimon profile image
Nurul Islam Rimon

Thanks, Syed Muhammad Ali Raza bro! That's right, but I love it and use it the most.

Collapse
 
odys profile image
Odys

Good to use!

Collapse
 
nurulislamrimon profile image
Nurul Islam Rimon

Thank you Odys💚