Disclosure: I am building BookFlow Pro, a paid booking template. AI assisted the project and this article. The frontend demo was checked, but the live Supabase integration and concurrency tests described below have not yet been run. This is a design discussion and test plan, not a production-validation claim.
A booking interface can show a time as available and still lose a race. Imagine two visitors loading the same calendar at 09:00. Both see 10:00 as free. Disabling the button after one visitor clicks does not update the other visitor's already-loaded page.
The useful distinction is between displaying availability and accepting a reservation. The display is a preview. The server must make the final decision and the interface must handle rejection without claiming that a booking succeeded.
Different start times can still conflict
Consider these two requests for a single shared calendar:
| Request | Start | End |
|---|---|---|
| Hair and beard | 09:00 | 10:00 |
| Haircut | 09:30 | 10:00 |
A uniqueness rule on the start timestamp alone would not detect this conflict. The starts differ, while the appointments overlap.
PostgreSQL range types and exclusion constraints provide a way to express non-overlap in the database. A half-open interval includes its start and excludes its end: a 09:00–10:00 appointment can sit next to a 10:00–10:30 appointment. See the official range constraint documentation.
In the supplied BookFlow schema, confirmed and completed reservations participate in the overlap constraint, while cancelled reservations are excluded. That preserves cancellation history without permanently blocking the old time. This schema is for one shared resource. Adding staff calendars requires explicitly scoping conflicts to the relevant resource; it is not just a new dropdown.
Availability must account for duration
The UI should not decide that 09:30 is available just because no reservation starts at 09:30. It needs to consider the proposed appointment's entire duration, existing intervals and closing time. A service that lasts 60 minutes cannot fit in the last 30 minutes of the working day.
After a booking succeeds, refresh availability. After a conflict, refresh it too and ask the visitor to choose another time. Keep the visitor's name and phone in the form so recovery does not require typing everything again. That is a recommended interaction, not a claim that every edge case in my template has been verified.
A practical acceptance checklist
These are tests I want completed against a fresh disposable backend before accepting real bookings:
- Submit two simultaneous requests for the same initially free appointment. Exactly one should succeed.
- Reserve 09:00–10:00, then request 09:30–10:00. The second must fail.
- Request an appointment starting at the previous appointment's end. It should succeed if business hours permit.
- Cancel the first booking and confirm its time becomes available again.
- Try a past date, a closed day and a service extending beyond closing time.
- Check permissions with an anonymous visitor, an authenticated non-admin and an admin. A hidden admin link is not access control.
- Check dates around daylight-saving changes in the business timezone.
The important result is the database state and permissions, not just a green toast in the browser.
What the demo can and cannot prove
My browser-only demo lets visitors inspect the booking interface and switch between English, Arabic and Hebrew. Its records stay in the visitor's browser. It does not prove database concurrency, RLS or real appointment delivery.
Try the interface demo. The optional BookFlow Pro source download costs $49, permits multiple client projects, and requires setup and hosting. It does not include staff calendars, payment processing or automated reminders.
What additional concurrency or permission test would you add before putting a guest-booking flow in front of real customers?
Top comments (0)