MongoDB and Laravel are a powerful combination — Laravel gives you elegant structure, while MongoDB offers flexibility and scalability. But what happens when your operations depend on multiple collections that must update together — for example, a booking confirmation that updates both rooms and guests?
That’s where MongoDB transactions come in. In this article, we’ll explore how to use transactions in Laravel with MongoDB, how they work, and how to use them safely in real-world applications.
What Are Transactions and Why They Matter
A transaction groups multiple operations into one atomic unit — meaning all succeed or all fail.
If something goes wrong halfway through, the transaction rolls back and no partial data is saved.
For example, when a guest books a hotel room, you need to:
Reserve the room.
Create the booking record.
Update the guest’s booking history.
If any of these fail, the others should roll back — otherwise you risk double-booking rooms or missing booking records.
MongoDB Transaction Support
MongoDB introduced multi-document transactions starting with version 4.0, bringing full ACID compliance (Atomicity, Consistency, Isolation, Durability) similar to relational databases.
To use transactions, you must run MongoDB as a replica set (even a single-node replica set works for development).
Laravel + MongoDB Setup
Install the Laravel MongoDB package:
composer require mongodb/laravel-mongodbThen configure your database connection in config/database.php:
'mongodb' => [
'driver' => 'mongodb',
'dsn' => env('MONGODB_URI', 'mongodb://127.0.0.1:27017/?replicaSet=rs0'),
'database' => env('MONGODB_DATABASE', 'hotel_booking_db'),
],And in your .env:
DB_CONNECTION=mongodb
MONGODB_URI=mongodb://127.0.0.1:27017/?replicaSet=rs0
MONGODB_DATABASE=hotel_booking_dbReal-World Example: Hotel Booking Transaction
Let’s look at a scenario where you’re building a hotel booking system.
When a user books a room, three collections are affected:
rooms→ mark the room as bookedbookings→ insert a new booking recordguests→ append the booking ID to the guest’s record
All of this must happen together — or not at all.
Here’s how you can do it in Laravel:
use Illuminate\Support\Facades\DB;
use MongoDB\BSON\ObjectId;
public function confirmBooking(string $roomId, string $guestId, string $checkIn, string $checkOut)
{
$connection = DB::connection('mongodb');
$client = $connection->getMongoClient();
$db = $connection->getMongoDB();
$session = $client->startSession();
try {
$session->startTransaction();
$rooms = $db->selectCollection('rooms');
$bookings = $db->selectCollection('bookings');
$guests = $db->selectCollection('guests');
// Check if room is available
$room = $rooms->findOne(['_id' => new ObjectId($roomId), 'status' => 'available'], ['session' => $session]);
if (! $room) {
throw new \Exception('Room not available.');
}
// Mark room as booked
$rooms->updateOne(
['_id' => new ObjectId($roomId)],
['$set' => ['status' => 'booked']],
['session' => $session]
);
// Create booking record
$booking = [
'room_id' => new ObjectId($roomId),
'guest_id' => new ObjectId($guestId),
'check_in' => $checkIn,
'check_out' => $checkOut,
'status' => 'confirmed',
'created_at' => now(),
];
$bookings->insertOne($booking, ['session' => $session]);
// Add booking reference to guest record
$guests->updateOne(
['_id' => new ObjectId($guestId)],
['$push' => ['bookings' => $booking]],
['session' => $session]
);
$session->commitTransaction();
return response()->json(['message' => 'Booking confirmed successfully!']);
} catch (\Exception $e) {
$session->abortTransaction();
return response()->json(['error' => $e->getMessage()], 500);
}
}
If any operation fails, everything rolls back — ensuring data consistency.
Tips and Best Practices
Use transactions only when needed
MongoDB documents are atomic by default. If all your updates are within a single document, you don’t need a transaction.Keep transactions short
The longer a transaction runs, the higher the chance of conflicts or timeouts.Always handle exceptions
Wrap your code intry-catchblocks and make sure you commit or abort properly.Ensure you’re using a replica set
Transactions won’t work in standalone MongoDB instances.Index frequently used fields
Especially fields you use in lookups during transactions, likeroom_idorguest_id.
Reusable Transaction Service (Clean Code Example)
To keep your controllers clean, you can abstract transaction logic into a service class:
namespace App\Services;
use Illuminate\Support\Facades\DB;
use MongoDB\Client;
class MongoTransactionService
{
public function run(callable $callback)
{
$client = DB::connection('mongodb')->getMongoClient();
$session = $client->startSession();
try {
$session->startTransaction();
$callback($session);
$session->commitTransaction();
} catch (\Throwable $e) {
$session->abortTransaction();
throw $e;
}
}
}Usage:
app(MongoTransactionService::class)->run(function ($session) {
// Your booking or cancellation logic here
});Wrapping Up
MongoDB transactions give you the best of both worlds — flexibility of NoSQL and reliability of SQL-like atomic operations.
In Laravel, it’s straightforward to use them with the right setup. Whether you’re building an e-commerce platform, a hotel booking app, or any multi-step workflow, MongoDB transactions ensure your data stays consistent and reliable.










