Overview
End-to-end clinic management system covering everything a dental clinic touches in a day: appointment scheduling, queue board, treatment plans, patient records, and finance. The frontend is a Next.js admin app, the API is an Express service running on AWS Lambda, and a separate Socket.io realtime service pushes queue and task updates the moment they happen. I owned all three pieces.
What I built
Clinical workflow
- Appointment scheduling with a calendar view and a live queue board for the front desk
- Treatment-plan builder with multi-step procedures, signed consent forms, and patient signature capture
- Patient records: demographics, medical history, photos taken in-browser via the device camera, and exportable PDF summaries
- Thai-language UI throughout, with Thai address picker and Thai date picker for cultural fit
Realtime queue + change streams
- Standalone Socket.io service (separate process, deployable via Docker) that subscribes to MongoDB change streams
- Any change to a task or queue record fans out to every connected client instantly — no polling
- Custom
useTaskSockethook on the frontend wires React state directly to socket events
Finance + admin
- Financial transactions, billing, and receipt generation with react-pdf
- ~38 configuration pages so the clinic can run their own taxonomies, fee schedules, and staff roles
- Backend organized as 40+ feature modules, each with its own model / route / controller / helper file
Tech stack
- Frontend: Next.js 14 App Router, React 18, Tailwind CSS, Radix UI, MUI for date pickers / big calendar
- State: Zustand stores + custom hooks per feature (
useTaskState,usePatientState) - Realtime: Socket.io client + a dedicated Socket.io server backed by MongoDB change streams
- Backend: Express on AWS Lambda, Mongoose with MongoDB
- PDF + media: react-pdf for documents, html2canvas, react-sketch-canvas for signatures, in-browser camera capture
- Infra: AWS Amplify host, API Gateway, CloudFront; Bun as the local toolchain
My role
End-to-end across three runtimes: the Next.js frontend, the Lambda Express API, and the Socket.io realtime service. Designed the data model and the module layout, wrote every backend module, and built the admin UI. The realtime service was a deliberate split — a long-running Socket.io process is a poor fit for stateless Lambda, so it became its own deployable backed by MongoDB change streams.
What I learned
The biggest lesson was treating the realtime layer as a separate service from day one, not a feature inside the main API. Socket.io needs a long-lived process; Lambda doesn't. By splitting it out and driving it from MongoDB change streams, the same database mutations that power the REST API automatically broadcast to live clients — no duplicated event-publishing logic on the write path. Building a system this broad (40+ backend modules, 38+ settings pages) also taught me how much module-per-feature consistency matters: .model / .route / .controller / .helper in every folder meant new features slotted in without rewiring anything.



