Search code examples
node.jsexpressmongoose

express-async-handler and mongoose session


In few videos I followed, they recommend to use express-async-handler but I am still not clear on it. But more importantly, how can one use it with sessions?

const proc = = expressAsyncHandler(async (req: Request, res: Response, next: NextFunction) => {
    const session = await mongoose.startSession();
    const newUser = await Data.create(..)
    const newGroup = await Group.create(...)
     session.commitTransaction()
}

now I could have an error in group so the whole thing should be rolled back. My error handler is defined in my entry file index.ts as follows

app.use((err: any, req: Request, res: Response, next: NextFunction) => {
  console.error(err.stack)
  res.status(500).send('Error Found')
})

so my question is...how can i do session abort?


Solution

  • The following post will try to address the second part of the question. Answer to the first part of this question has already been posted separately. It is a prerequisite to at least skim through it in order to proceed with this part.

    Now handling errors in this question, the original code would need to be refactored. Please see the respective codes below.

    The original code

    const proc = = expressAsyncHandler(async (req: Request, res: Response, next: NextFunction) => {
        const session = await mongoose.startSession();
        const newUser = await Data.create(..)
        const newGroup = await Group.create(...)
         session.commitTransaction()
    }
    

    Refactored - code using express-async-handler

    const proc1 = expressAsyncHandler(async (req, res, next) => {
      try {
        const session = await mongoose.startSession();
        try {
          const newUser = await Data.create('..');
          const newGroup = await Group.create('..');
          await session.commitTransaction();
        } catch (error) {
          await session.abortTransaction();
          throw error;
        }
      } catch (error) {
        throw error
      } finally {
        session.endSession();
      }
    });
    

    Alternatively, without using the NPM module express-async-handler and by following the manual way “catch and pass the error” as in server3.js, the following refactoring would be required.

    Refactored - code using manual error handling

    const proc2 = async (req, res, next) => { 
      try {
        const session = await mongoose.startSession();
        try {
          const newUser = await Data.create('..');
          const newGroup = await Group.create('..');
          await session.commitTransaction();
        } catch (error) {
          await session.abortTransaction();
          next(error);
          return;
        }
      } catch (error) {
        next(error);
        return;
      } finally {
        session.endSession();
      }
    };
    

    Notes:

    1. The error handled in the above codes, would invoke the custom error handler defined in index.js. This is irrespective of the files in which these refactored codes are hosted. And this is the basic feature of Express error handling mechanism.

    2. Aborting a session is always done in catch blocks as shown in the codes. Please also note the placement commit and session ending statements as well.

    3. The codes given above have not been tested.

    4. For a full listing of tested codes handling a similar requirement, please refer to the post given under Links section.

    5. For brevity, the types have been avoided from the refactored codes.

    Links:

    1. Abort transaction friendly way to update multiple documents in mongodb?