UNPKG

tranxpress

Version:

A smart async wrapper for Express with MongoDB transaction support.

73 lines (63 loc) 2.01 kB
import { Request, Response, NextFunction } from "express"; import mongoose, { ClientSession } from "mongoose"; import { AsyncHandlerOptions, AsyncFn } from "../types/sharedTypes"; import { MongoHandledError } from "../utils/mongoErrorHandler"; export const asyncHandler = ( fn: AsyncFn, options: AsyncHandlerOptions = {} ): AsyncFn => { const { useTransaction = false, customErrorHandler } = options; return async ( req: Request, res: Response, next: NextFunction ): Promise<void> => { let session: ClientSession | null = null; try { if (useTransaction) { session = await mongoose.startSession(); session.startTransaction(); req.session = session; } await fn(req, res, next); if (session) { await session.commitTransaction(); session.endSession(); } } catch (error: any) { console.error("❌ Error caught in asyncHandler:", error); if (session) { try { await session.abortTransaction(); session.endSession(); } catch (abortErr) { console.error("⚠️ Failed to abort transaction:", abortErr); } } // Wrap known MongoDB errors if ( error.name === "MongoServerError" || error.name === "MongoError" || error.name === "CastError" || error.name === "ValidationError" ) { error = new MongoHandledError(error); } // 🔁 Support nested asyncHandlers if (typeof customErrorHandler === "function") { try { const result = customErrorHandler(error, req, res, next); if (result instanceof Promise) { await result; } } catch (customHandlerError) { console.error("⚠️ Error inside customErrorHandler:", customHandlerError); } } return next({ message: error?.message || "Internal Server Error", statusCode: error?.statusCode || 500, }); } }; };