Skip to content
← All work

QueueLite

Live in production

Queue management for small clinics

Frontend

React 19 · Vite

Backend

Express 5 · Node 20

Data

PostgreSQL · Supabase

Hosting

Vercel · Render

01The problem

Small clinics run their queue on a paper register and a raised voice. Nobody knows how long they'll wait, so nobody leaves the room.

What's actually scarce: the doctor's time, or knowing where you stand?

02The solution

Show everyone where they stand: a desk for staff to issue tokens, a board for the waiting room, and a tracker on each patient's phone.

Live at Dev Eye Care, Moradabad, handling 30–40 patients a day.

03Architecture

React on Vercel, an Express API on Render, Postgres on Supabase. Vercel proxies /api to Render, so it runs as one origin: no CORS, first-party cookies.

10s10s10s → 2m40same originserve1 readwrites · invalidateStaff deskReact 19 · ViteWaiting boardReact 19 · VitePatient trackerReact 19 · ViteVercelrewrites /api/*Express 5 APINode 20 · RenderSnapshot2.5s, in processPostgreSQLSupabase · service role

One origin, three surfaces on three different clocks, and a snapshot cache so a full waiting room costs one database read. Connections: Staff desk to Vercel (10s); Waiting board to Vercel (10s); Patient tracker to Vercel (10s → 2m40); Vercel to Express 5 API (same origin); Express 5 API to Snapshot (serve); Snapshot to PostgreSQL (1 read); Express 5 API to PostgreSQL (writes · invalidate).

scroll the diagram sideways →

BrowserServiceGate / cacheDataExternaldashed = on a timer

04Technical depth

  • A unique index on (clinic_id, token_day, token_number) makes duplicate tokens impossible; a clash just retries.
  • The bug that actually bit was IST midnight, not a race. Fixed by storing the day instead of deriving it.
  • Polling, not WebSockets: every 10s near the front of the queue, stretching to 2m40 at the back.
  • A 2.5s snapshot means a whole waiting room refreshing at once costs one database read.
A rule in application code is one you have to remember. A rule in the database remembers itself.