← All work
Case studyHealthcare · Patient booking · Bilingual web

Hospital Website & Serial Booking System

A bilingual (English and Bengali) website and patient-booking system for a mother-and-child hospital with 25 consultants. Patients book a serial number online, and reception issues numbers from the same table at the counter, so the two can never hand out the same number.

The hard part is not the website. It is a queue that two channels share without ever clashing, patient data that only the right staff can see, and a system the hospital can run on its own after handover.

Role
Design, engineering & coding
Domain
Healthcare · patient booking
Languages
English · বাংলা
Status
Built · yet to go live
The problem
The hospital had no online booking, and its old website could not be updated without a developer. Any booking system had to fit how the chambers already run: serial numbers, not appointment times.
What was built
Morsalin designed and built a bilingual Nuxt and PostgreSQL platform where patients and reception claim serials from one table, with role-based access, audit logging and SMS and email notifications.
The result
Built and ready for launch, pending the hospital's final information. Staff can run the whole site — consultants, schedules, fees and notices — from the admin, without a developer.
25consultants
2booking channels, one queue
10staff roles
20+admin screens
3SMS gateways
305automated tests
2languages, data included
~27.6klines of code
01The problem

Online booking for a clinic that runs on serial numbers.

01

Patients could not book online.

The hospital had no booking of any kind. Patients phoned or queued at the counter to get a serial number for a consultant’s sitting.

02

The chambers run on serials, not appointments.

A patient is given number 14 for the evening sitting and seen when 14 is called. A clock-time appointment system would promise a precision the clinic cannot keep.

03

Nobody would be there to change it.

There is no technical team after handover. Anything an administrator might need to change — consultants, schedules, fees, notices, images — had to be editable without a developer, or it would never change.

02Serial booking

One queue, shared by the website and the counter.

One table, two channels

Online and counter bookings both claim serials through one function, inside a serializable transaction, with a unique index on sitting, date and serial as the database-level backstop. The website and the front desk cannot issue the same number.

The counter outranks the website

Reception may overfill a sitting and may book one already under way; the website may do neither. A walk-in reserve, bounded by both the online quota and the room physically left, keeps space for patients who arrive in person.

Retired numbers stay retired

A cancelled booking gives up its place but keeps its number, so two paper slips never read the same serial. The search window widens by the retired count, so a sitting for 30 still seats 30.

Each patient gets their own estimate

Patients are told when their serial is expected, not when the next unsold number is. Running-late messages go out only on a real change, so a queue is not re-texted on every “call next”.

03Access & audit

Patient data that only the right staff can see.

10 roles, around 48 permissions

Roles are composed from a permission catalogue. The admin UI shows only what a role can use, but that is a courtesy — the refusal happens in the server route.

“Whose patients” as row-level scope

An assistant and a doctor’s own assistant hold nearly the same permissions and differ only in which doctors’ patients they may see. That scope is enforced on every query, not by a permission.

Every access is audited

Each read or write of patient data is written to an audit log — including denials — so a question about who saw a record has an answer.

Sessions follow the role

Demoting a user revokes their existing sessions, rather than leaving the old permissions live until their next sign-in.

04Notifications

Messages that help, and never block a booking.

Email and SMS, three gateways

SMTP plus three Bangladeshi SMS providers, configured from the admin. Gateway credentials are stored in the database, encrypted at rest with AES-256-GCM.

A failed message never fails a booking

The serial is issued either way. The failure is recorded, so reception has a call list instead of a patient who never heard back.

Secrets stay out of error messages

Gateway errors are caught and rethrown without the request URL, so an outage cannot print a live API key into the admin’s browser or a proxy log.

Mandatory means mandatory

A cancellation SMS is required because the patient may already be travelling; it is not considered delivered just because the email went through.

05Built to degrade

A problem breaks one feature, never the emergency page.

The emergency page outlives the server

It is prerendered and long-cached, so a CDN keeps serving it for a week after the origin goes down — on a bad connection, at 2am.

Missing configuration degrades one feature

The database client is created on first use and a boot check names every missing setting without exiting, so a bad deploy cannot take the static pages down with it.

A broken image library breaks only images

The image library is loaded lazily. With it removed entirely, the site still starts and still serves its pages; only image processing fails.

Uploads are checked four ways

Extension, declared type and file signature must agree, with an image decode as a fourth check. Private files live behind separate routes and are unreachable from the public ones.

06Run by the hospital

Everything staff need, nothing that needs a developer.

Public site

Consultant directory with schedules and fees, departments, centres, packages, a baby-growth tool and an emergency page — in English and Bengali, with separate localised routes.

Patient booking

Choose a consultant and sitting, book a serial at any hour, and look the booking up later by reference and mobile number.

Reception and waiting room

A counter queue for issuing and calling serials, printable slips, and a display board for the waiting room.

Admin without a developer

20+ screens for consultants, schedules, sittings, fees, notices, media, branding, staff, roles and integrations — plus plain-language guides written for staff, one page per job.

07Key decisions

Every non-obvious choice, with its reason.

Serials, not clock times

It is how the chambers run. An estimate per serial is honest; a 3:15pm appointment would not be.

One allocator for both channels

Two allocators would eventually disagree. One function and one unique index make a double-issued number impossible, not just unlikely.

Permissions checked in the route

A hidden menu item is not access control. Every admin endpoint refuses on the server.

Bilingual columns, not a translation layer

A specialty exists in both languages or is honestly missing in one — never silently shown in the wrong language.

Degrade, never crash

A configuration or infrastructure problem may break only what depends on it. The emergency page must survive everything.

Everything editable in the admin

There is no operations team after handover. What cannot be edited will not be kept current.

Notifications are best effort, bookings are not

An SMS outage should cost a phone call, not a patient’s serial.

Two images from one Dockerfile

The web server ships without database tooling; migrations and seeds run from a separate tools image and exit.

08Outcome

Ready to launch, and run by the hospital.

Online booking

Patients can book a serial at any hour, from the same queue reception uses.

One queue, no clashes

Website and counter can never issue the same number for the same sitting.

Audited patient data

Role- and scope-based access, checked on the server, with every access logged.

Owned by the hospital

Content, people, schedules and fees are all editable by staff from the admin.

  • Status: built and reviewed, yet to go live. The remaining work is the hospital’s own information — consultant fees and schedules — not code.
  • Replaces a site with no booking whose contact details were hardcoded, and whose emergency page froze mobile browsers.
  • A deployment runbook, a rehearsed first deployment and a backup-restore drill are part of the handover.
  • Test coverage is stated plainly, including the gaps: concurrency under real load and route-level permission tests are the next things to automate.

My role

I designed, built and coded the whole system: the booking model and its allocator, the access-control and audit design, the notification layer and its failure handling, the bilingual data model, the media pipeline, the admin and staff tools, and the Docker deployment with its runbook and restore drill. I also wrote the technical overview and the plain-language staff guides that go with the handover.

Stack

App
Nuxt 4 · Vue 3 · TypeScript · Tailwind CSS
Data
PostgreSQL · Prisma 7 · Zod
Security
Sealed sessions · CSRF · rate limits · AES-256-GCM
Ops
Docker · SMTP · SMS · Vitest
Nuxt 4Vue 3TypeScriptPostgreSQLPrismaBooking systemsConcurrencyAccess control (RBAC)Audit loggingInternationalisationSMS & emailEncryption at restFile-upload securityDockerVitest

Described generically — the hospital is not named while the site is awaiting launch. One-page summary (PDF)

Next

Need a booking system people actually use?

Get in touch →
© 2026 MD Morsalinbuilt with care · OSS-first · sustainable by design
lat: 23.81°Nlon: 90.41°Etz: UTC+6status: 200 OK