Simplified MongoDB Transactions in Express.js

Simplified MongoDB Transactions in Express.js

Beginner's Guide to Implementing MongoDB Transactions in Express.js

Introduction

MongoDB is a popular NoSQL database known for its flexibility and scalability. With the introduction of multi-document transactions in MongoDB 4.0, developers gained more power to perform complex operations reliably. In this blog post, we will delve into MongoDB transactions, explore their significance, and provide detailed examples for better understanding.

Understanding Transactions

In database management, a transaction is a sequence of operations that are treated as a single unit of work. These operations either succeed together or fail together. Transactions ensure data integrity by maintaining the ACID properties: Atomicity, Consistency, Isolation, and Durability.

Transactions are essentially sequences of database operations that are treated as a single unit. They adhere to the ACID properties (Atomicity, Consistency, Isolation, Durability):

  • Atomicity: Either all operations within a transaction succeed, or none of them do. This prevents partial updates that could leave your data in an inconsistent state.

  • Consistency: Transactions maintain the validity of your database schema. They ensure that data transitions from one consistent state to another.

  • Isolation: Transactions execute in isolation from other concurrent operations, safeguarding data integrity and preventing unintended modifications during the transaction's execution.

  • Durability: Once a transaction commits successfully, the changes are persisted durably to the database, guaranteeing their survival even in the event of system failures.

Why Use Transactions?

While MongoDB excels at storing related data within documents, ensuring consistency across multiple documents in separate collections can be challenging without transactions. Consider a scenario where you need to create a new user account:

  1. Insert a document into the users collection containing name, email, and phone information.

  2. Insert a separate document into the address collection storing address1, address2, city, and pincode details.

If these operations were not part of a transaction, there's a potential for inconsistency. Imagine a situation where the user document is inserted into users but a system crash occurs before the corresponding address document is added to address. This would leave your data in an incomplete and inaccurate state.

Without transactions, if an error occurs after adding the user's information but before storing their address, the database would be left in an inconsistent state. Transactions help address this issue by ensuring that either all operations related to adding a user succeed or none of them do.

Transactions address this by ensuring that both operations succeed or fail together. If an error occurs during either insertion, the entire transaction rolls back, reverting any changes made. This guarantees data consistency and eliminates the risk of partial updates.

Implementing Transactions in MongoDB

Step 1: Setting Up the Project

  1. Open your terminal.

  2. Create a new directory for your project: mkdir mongodb-transaction-express.

  3. Navigate into the project directory: cd mongodb-transaction-express.

  4. Initialize a new Node.js project: npm init -y.

Step 2: Installing Dependencies

Install required dependencies: npm install express mongoose

Step 3: Creating the Express Application

  1. Create a new file named app.js.

  2. Open app.js in your code editor.

  3. Initialize Express and set up basic middleware:

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.use(express.json());

Step 4: Setting Up MongoDB Connection

  1. Create a new file named db.js.

  2. Open db.js in your code editor.

  3. Set up MongoDB connection using Mongoose:

const mongoose = require('mongoose');
const MONGODB_URI = 'mongodb://localhost:27017/my_database';

mongoose.connect(MONGODB_URI, {
    useNewUrlParser: true,
    useUnifiedTopology: true
});

const db = mongoose.connection;

db.on('error', console.error.bind(console, 'MongoDB connection error:'));
db.once('open', () => {
    console.log('Connected to MongoDB database');
});

module.exports = db;

Step 5: Creating MongoDB Models

  1. Create a new directory named models.

  2. Inside the models directory, create two files: User.js and Address.js.

  3. Define schemas and models for User and Address: models/User.js:

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true,
        trim: true,
        unique: true
    },
    phone: {
        type: String,
        required: true
    }
});


const userModal = mongoose.model('User', userSchema);

module.exports = userModal;

models/Address.js:

const mongoose = require('mongoose');

const addressSchema = new mongoose.Schema({
    userId: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        required: true,
        unique: true,
    },
    address1: {
        type: String,
        required: true
    },
    address2: {
        type: String,
    },
    city: {
        type: String,
        required: true
    },
    pincode: {
        type: String,
        required: true
    }
});

const addressModal = mongoose.model('Address', addressSchema);
module.exports = addressModal;

Step 6: Implementing Routes and Controllers

  1. Create a new directory named routes.

  2. Inside the routes directory, create a file named index.js.

  3. Define routes and controllers for creating a new user with address: routes/index.js:

const express = require('express');
const router = express.Router();
const db = require('../db');
const userModal = require('../models/User');
const addressModal = require('../models/Address');

router.post('/create-user', async (req, res) => {
    const session = await db.startSession();
    session.startTransaction();

    try {
        const { name, email, phone, address1, address2, city, pincode } = req.body;

        // Check if the email already exists
        const existingUser = await userModal.findOne({ email });
        if (existingUser) {
            res.status(500).send('User already exists.');
        }

        // Create user document
        const user = await userModal.create([{ name, email, phone }], { session });

        // Create address document
        await addressModal.create([{ userId: user[0]._id, address1, address2, city, pincode }], { session });
        console.log({user})
        await session.commitTransaction();
        res.status(201).send('User created successfully.');
    } catch (error) {
        await session.abortTransaction();
        console.error('Transaction aborted. Error:', error);
        res.status(500).send('Failed to create user.');
    }
});

module.exports = router;

Step 7: Mounting Routes in Express Application

  1. Open app.js.

  2. Mount the routes:

const indexRouter = require('./routes/index');

app.use('/', indexRouter);

Step 8: Running the Express Application

  1. In the terminal, run the following command: node app.js

Your Express application is now running on http://localhost:3000.

Now open your postman or Insomnia to test this endpoint and pass the data and play around, and check if you can send data in only one of the collection or not.

Conclusion

Congratulations🥳! You've successfully implemented MongoDB transactions in an Express.js application. By following this beginner's guide, you've learned how to set up the project, create MongoDB models, define routes and controllers, and run the Express server. Transactions ensure data integrity and consistency, making your application more robust and reliable. Experiment further with MongoDB transactions to enhance your applications even more on the official MongoDB docs.

To read more about tech, web development & open source, you can follow me on Hashnode and Twitter (@MadhuSaini22) and If this blog helped you in any way then you can sponsor my work and show love and support.

Thank you so much for reading! 👩‍💻

Did you find this article valuable?

Support Madhu Saini by becoming a sponsor. Any amount is appreciated!