It’s the notification every marketplace founder dreads: "Hey, why is someone else driving the car I just booked?"

Early on with Gaari (my car rental platform), we hit a critical edge case. Two customers, miles apart, hit "Book Now" on the same Toyota Corolla for the same weekend, at the exact same millisecond.
The database didn't blink. It said "Yes" to both.
That’s when I learned that
if (isAvailable)is not a valid way to check inventory in the real world.
I spent the next two weeks tearing down our naive booking logic and rebuilding it with Next.js 15, Supabase, and some serious PostgreSQL locking strategies.
Here is the architecture that finally let me sleep at night:
1. The Database Schema (The Foundation) We stopped storing "Available Dates" and started storing "Ranges". PostgreSQL's
TSTZRANGEis powerful. instead of checking 100 individual rows for availability, we check for overlaps:
sql-- The "No Overlap" Constraint
CONSTRAINT no_double_booking EXCLUDE USING gist (
item_id WITH =,
booking_range WITH &&
);
This one constraint does more work than 500 lines of JavaScript. It makes double-booking physically impossible at the database level.
2. Optimistic vs. Pessimistic Locking We moved the availability check inside the transaction. When a user starts checkout:
We don't "reserve" the car (locks inventory too long).
We check availability efficiently.
The Stripe Integration: We use Webhooks to finalize the booking. Only when
checkout.session.completedfires do we insert the final row.
3. Client-Side State (Next.js 15) We handle the real-time aspect using Supabase Realtime under the hood. If someone else is viewing the same car, you see a live "3 people are looking at this" indicator. It’s not just FOMO; it’s an early warning system for race conditions.
Building a booking system isn't about the "Happy Path" where one user books one car. It's about the "Chaos Path":
Timezones (Dhaka vs. Chittagong seemed simple until Daylight Savings hit our international clients).
Payment failures holding inventory hostage.
The "Back Button" problem.
Today, Gaari handles thousands of bookings without a single overlap. Robust engineering isn't flashy, but it’s the only reason our business exists.
I wrote a 3,000-word deep dive on the code, database schema, and transaction logic. It’s open-source knowledge for anyone building a rental platform: https://portfolio-rizwanul.vercel.app/blog/how-to-build-booking-system-nextjs
0
3
0