Wednesday 29 June 2022

Use transaction session in try/catch function from wrapper

In multiple functions I'm running more than one database action. When one of these fails I want to revert the ran actions. Therefore I'm using a transaction session from Mongoose.

First I create a session with the startSession function. I've added the session to the different Model.create functions. At the end of the function I'm committing and ending the session.

Since I work with an asyncHandler wrapper on all my function I'm not retyping the try/catch pattern inside my function. Is there a way to get the session into the asyncHandler of a different wrapper to abort the transaction when one or more of these functions fail?

Register function example

import { startSession } from 'mongoose';
import Company from '../models/Company';
import Person from '../models/Person';
import User from '../models/User';
import Mandate from '../models/Mandate';
import asyncHandler from '../middleware/asyncHandler';

export const register = asyncHandler(async (req, res, next) => {    
    const session = await startSession();
    
    let entity;

    if(req.body.profile_type === 'company') {
        entity = await Company.create([{ ...req.body }], { session });
    } else {
        entity = await Person.create([{ ...req.body }], { session });
    }

    // Create user
    const user = await User.create([{ 
        entity,
        ...req.body
    }], { session });

    // Create mandate
    await Mandate.create([{
        entity,
        status: 'unsigned'
    }], { session });

    // Generate user account verification token
    const verification_token = user.generateVerificationToken();

    // Send verification mail
    await sendAccountVerificationMail(user.email, user.first_name, user.language, verification_token);

    await session.commitTransaction();
    session.endSession();

    res.json({
        message: 'User succesfully registered. Check your mailbox to verify your account and continue the onboarding.',
    })
});

asyncHandler helper

const asyncHandler = fn => ( req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);

export default asyncHandler;

EDIT 1

Let me rephrase the question. I'm looking for a way (one or more wrapper functions or a different method) to avoid rewriting the lines with // ~ repetitive code behind it. A try/catch block and handling the start and abort function of a database transaction.

export const register = async (req, res, next) => {    
    const session = await startSession(); // ~ repetitive code
    session.startTransaction(); // ~ repetitive code

    try { // ~ repetitive code     
        let entity;

        if(req.body.profile_type === 'company') {
            entity = await Company.create([{ ...req.body }], { session });
        } else {
            entity = await Person.create([{ ...req.body }], { session });
        }

        const mandate = await Mandate.create([{ entity, status: 'unsigned' }], { session });

        const user = await User.create([{ entity, ...req.body }], { session });
        const verification_token = user.generateVerificationToken();
        
        await sendAccountVerificationMail(user.email, user.first_name, user.language, verification_token);

        await session.commitTransaction(); // ~ repetitive
        session.endSession(); // ~ repetitive

        res.json({
            message: 'User succesfully registered. Check your mailbox to verify your account and continue the onboarding.',
        });
    } catch(error) { // ~ repetitive
        session.abortTransaction(); // ~ repetitive
        next(error) // ~ repetitive
    } // ~ repetitive
};


from Use transaction session in try/catch function from wrapper

No comments:

Post a Comment