Why I'm Building an Appointment Scheduler
I’ve spent years working in healthcare SaaS. Not as a domain expert — as the person responsible for the systems that power patient-facing workflows, including the scheduling layer. And the thing about scheduling software is that it fails in the same ways, over and over, across every product I’ve encountered in that space.
The failure isn’t usually dramatic. It’s quiet: two front-desk staff booking the same slot from different tabs, a provider’s availability pattern out of sync with what the UI is offering, a validation rule living in the application layer that should have been a database constraint. The system works until it doesn’t, and when it doesn’t, a real person has to apologize to a real patient.
I’ve fixed these problems. What I want to do now is build a system where they can’t exist in the first place.
The plan
The project is called rhizo-book, and it’s a full-stack healthcare appointment scheduler built with NestJS, Next.js 15, PostgreSQL, and Prisma — TypeScript end to end.
The core design decision is about what the data model makes impossible. Availability and bookings are distinct concepts that most schedulers conflate, and that conflation is where the bugs live. My plan is to model them separately: providers have availability schedules, which generate concrete time slots, which can be claimed atomically. Conflict prevention lives at the database layer — a unique constraint on provider and start time — so two concurrent booking attempts produce one success and one clean rejection, regardless of what the application code thinks it’s doing.
That’s the part I care about getting right. The rest — auth, role separation, appointment management — is table stakes for any scheduling product, and I’ll build it to a high standard, but the data model is where the real thinking is.
Why TypeScript end to end
I work across stacks. The language I reach for depends on the problem. For this project, TypeScript throughout means the type contracts are real: the same interface that defines a TimeSlot in the backend schema shows up in the frontend booking flow. That’s not just ergonomics — it means a class of errors that usually shows up at runtime gets caught at compile time, which changes how you design the system.
NestJS gives me the module boundaries and dependency injection structure I want for the backend without fighting me. Next.js 15 on the frontend. Prisma for the ORM, because the generated types are a genuine asset when you’re working across a full TypeScript stack and care about query correctness.
The UI is not an afterthought
Most scheduling UIs are functional in the way a government form is functional. They do the job, technically.
I want rhizo-book to look like something I’d actually want to use. The booking flow should feel clear. The provider dashboard should communicate state at a glance. I’m investing real time in the frontend — not because it’s a portfolio piece, but because the UI is part of the product, and I have opinions about it.
The live project is at rhizobook.cyberrhizome.ca and the API is fully documented via Swagger. The frontend is where most of the remaining work is, and that’s by design — I want to get the data layer right before I spend cycles on the UI.
Why build it instead of describing it
I’ve solved versions of this problem in production. That work was real, and the outcomes were real, but the code isn’t mine to show.
rhizo-book is the version where every decision is mine. The schema choices, the auth flow, the API contracts. When something is wrong I find it — I don’t work around someone else’s constraints. That’s a different kind of ownership than working inside an existing system, and I think it reads differently when you’re looking at a pull request.
That’s the point. I want to show how I think, not just tell you about it.
// comments via github discussions