Click Below to subscribe

How to implement Transactions in Sequelize & Node.Js (Express)

Transaction is a way to execute or commit a group of operations as a unit. In other words, it is a technique to call multiple SQL statements as a single unit.

In case of the transaction if any error occurred all the operations rollback.

Sequelize provides a very easy way to implement transactions.

# Without Transaction

const User = require("../models/user.model");
const ShippingAddress = require("../models/address.model");

async function register() {

    try {
        // Create User
        const user = await User.create({
            name: 'Van Helsing'
        });

        // Create Shipping Address
        const address = await ShippingAddress.create({
            address: 'Transylvania',
            user_id: user.id
        });
        console.log('success');
    } catch (error) {
        console.log('error');
    }
}

In the above example, we can face two error cases.

1.  user creation operation is not executed.

2.  user is created but the shipping address operation is not executed.

So to prevent this type of scenario we need to use transactions.

# Transactional Toolset

1. Creating a transaction

2. Rollback a transaction

3. Committing a transaction

# Implementation - 

models/connection.js
const Sequelize = require('sequelize'); 
const conn = {};
const sequelize = new Sequelize('my_db', 'username', 'password', {
    host: 'localhost',
    dialect: 'mysql',
    operatorsAliases: 'false',
    logging: false
});

conn.sequelize = sequelize;
conn.Sequelize = Sequelize;

module.exports = conn;

1. Managed Transactions - Auto callback method

const { sequelize } = require("../models/connection"); 
const User = require("../models/user.model");
const ShippingAddress = require("../models/address.model");

async function register() {

    try {
        await sequelize.transaction(async function (transaction) {
            // chain all your queries here. make sure you return them.
            const user = await User.create({
                name: 'Van Helsing'
            }, { transaction });
            
            await ShippingAddress.create({
                address: 'Transylvania',
                user_id: user.id
            }, { transaction });
            
            return user;
        });
        console.log('success');
    } catch (error) {
        console.log('error');
    }
}

In this way, the transaction will automatically be committed. You don't need to worry about manually rolling back or committing while using the transaction method

2. Unmanaged Transactions - Manual method (Recommended)

In this way, we have more control over the operations. so I personally prefer this way to implement transactions. we have three methods for manually controlling the operations. 

await sequelize.transaction();  // for starting a new transaction
await transaction.commit();     // for committing all operations
await transaction.rollback();   // for rollback the operations

const { sequelize } = require("../models/connection"); 
const User = require("../models/user.model");
const ShippingAddress = require("../models/address.model");

async function register() {

    let transaction;
    try {
        transaction = await sequelize.transaction();
        const user = await User.create({
            name: 'Van Helsing'
        }, { transaction });

        await ShippingAddress.create({
            address: 'Transylvania',
            user_id: user.id
        }, { transaction });

        console.log('success');
        await transaction.commit(); 

    } catch (error) {
        console.log('error');
        if(transaction) {
           await transaction.rollback();
        }
    }
}

In the above example, we have full control over the operation so we can easily handle errors based on what error occurred.

#Extras:-

const { sequelize } = require("../models/connection"); 

async function example() {
    let transaction;
    try {
        transaction = await sequelize.transaction();
        
        await Model.create({ /* payload */ }, { transaction});

        await Model.destroy({ where: { /* conditions */ }, transaction });

        await Model.update({ /* payload */ }, { where: { /* conditions */ }, transaction });

        await transaction.commit();

    } catch (err) {
        if(transaction) {
           await transaction.rollback();
        }
    }
}

Leave Your Comment