Tunas Akara
Back to Blog

Building a Hotel Management System for a Real Hotel

by RayhanUpdated 7 min read
hotel managementnextjspostgresqldrizzlewhatsapphospitality
Building a Hotel Management System for a Real Hotel

Building a Hotel Management System for a Real Hotel

Hospitality software is not just rooms and bookings. The hard part is modeling the hotel's operational reality. A room can be reserved, checked in, dirty, under maintenance, blocked for an owner, or ready but waiting for inspection.

That's what I faced building a hotel management system for a hotel in Pekalongan. Front-desk staff needed a simple screen. The database needed enough structure that small mistakes never turned into guest complaints. For the broader picture, I also wrote a separate hotel management system guide.

I built it with the hotel team: Next.js 16 for the app layer, PostgreSQL for durable records, Drizzle ORM for type-safe access. The goal was not a generic booking app. It was a system that could survive daily operations: walk-in guests, long-stay guests, housekeeping pressure, multi-language workflows, and knowing what happened when something went wrong.

Start With the Room, Not the Booking

The first design decision was to make room status explicit. Many early hotel systems only know "available" or "occupied." That's not enough. A real room has several independent dimensions:

  • Reservation availability: can this room be sold for a date range?
  • Physical status: clean, dirty, inspected, maintenance, out of order.
  • Operational status: ready for check-in, waiting for inspection, blocked.
  • Guest status: checked in, checked out, no-show, extended stay.

Think of room status like a traffic light with more than three colors — each color can only change through a defined path, never skip ahead. That's why I modeled room status as a state machine instead of a free-text field. A room moves from Dirty to Cleaning, then Inspected, then Ready. A room marked OutOfOrder cannot jump to Ready until maintenance is done and an authorized user clears it.

This stopped housekeeping from accidentally prepping a room that engineering had blocked for repair. I go deeper on this transition pattern in modeling hotel room status as a state machine.

The same idea applied to reservations. A reservation is not just "booked." It can be Held, Confirmed, CheckedIn, CheckedOut, Cancelled, NoShow, or Extended. Every transition is recorded with an actor, timestamp, and reason. That audit trail matters when the front desk has to explain why a room's status changed or why a booking got cancelled.

Preventing Overbooking Before It Reaches the Guest

Overbooking prevention was the most critical requirement. The system had to handle date ranges, room types, walk-in bookings, and last-minute changes. Two guests must never land in the same physical room. I broke down the full strategy in preventing hotel overbooking.

The key move: separate room-type inventory from physical room assignment. A guest can book a "Deluxe Twin" long before the hotel picks a room number. The database checks available inventory for the requested dates first.

The physical room only gets assigned closer to check-in or when front desk confirms. Capacity stays protected without locking a specific room too early.

For exact room assignments, I used database constraints and transaction-level checks. One room can never carry two overlapping active assignments. The app also holds a short-lived reservation lock while a front-desk employee is building a booking. That way, two staff members can't complete conflicting check-ins at the same time.

Overbooking prevention can't rely on UI logic alone. A button can say "available" and still be stale by the time someone clicks it. The real rule has to live in the database.

Multi-Language Workflows Without Duplicating the Product

The hotel needed EN, ID, and CN labels across the operational flow. Some staff were more comfortable in Indonesian, some guests needed Chinese labels, management wanted English reports. I didn't build separate screens per language. The data model stayed language-neutral; language moved into labels, messages, and generated documents.

Room types, service names, receipt lines, and notification templates were all stored with translation keys. The UI just rendered the active language. The database held one canonical record. One room type had one ID, one pricing rule, one status history — but could display in several names at once.

This paid off for WhatsApp too. A booking confirmation went out in the guest's preferred language. Internal front-desk notifications stayed in Indonesian. The system never translated business rules, only the layer that faced humans.

Permissions That Match Hotel Roles

A hotel system serves very different users. The owner wants revenue visibility. The manager needs override power. Front desk needs booking and check-in workflows.

Housekeeping needs to update room status. Maintenance needs repair tasks. Accounting needs invoices and payment records.

I deliberately avoided a single admin role. Permissions were grouped by capability: booking:create, booking:cancel, room:change_status, room:block, invoice:view, report:revenue, system:configure. Each role was assembled from these capabilities. Housekeeping could get room-status access without ever seeing revenue reports or system settings.

The practical payoff was trust. Every staff member used the system for their own work, without exposure to data that wasn't theirs. Managers could still audit every override.

WhatsApp as an Operational Layer

WhatsApp automation became more than a notification feature. It bridged the system to how the hotel actually worked day to day.

For guests, WhatsApp handled booking confirmations, pre-arrival reminders, room-ready messages, and checkout follow-ups. For staff, it became the alert channel for high-priority events. That meant VIP arrival, a room stuck too long in Cleaning, maintenance escalation, or a front-desk task needing attention.

The principle I held to: every WhatsApp message had to trace back to a business event. A message carried a recipient, template, related booking or room, delivery status, and retry state. If a notification failed, front desk had to know. If a guest replied, the conversation had to link back to the right guest record.

Lessons From Deploying in a Real Hotel

The real challenges only showed up after launch. A system that runs smoothly in a demo can stumble in the field. The real environment has slow internet, shared devices, a busy front desk, and staff who need fast answers.

So I optimized for clarity over cleverness. Housekeeping screens got large buttons and room colors that read at a glance. Manager revenue reports matched their daily closing routine.

The database got indexes for date-range queries, because availability checks run almost constantly. Audit logs stayed simple enough to read fast during an incident.

The biggest architectural lesson: a hotel management system can't just store transactions. It has to guide the next action. When a guest checks out, the room turns dirty and housekeeping gets a task automatically.

When a room turns ready, front desk knows. When a booking is confirmed, the guest gets a WhatsApp message. The system earns its value when operational events flow automatically from one team to the next.

The system worked because the architecture followed the hotel's real workflow, not the other way around. Next.js gave the team a fast interface. Drizzle and PostgreSQL kept the data reliable. WhatsApp connected the software to the people who needed it most.

Related Posts

Building something similar?

Hotel Management System Development

Custom ERP-style hotel management software: bookings, room status, invoicing, staff, and WhatsApp automation — built around how your hotel actually runs.

See how I can help