The Challenge
Dental clinics juggle patient records, appointment calendars, treatment tracking, lab orders, and financial management — often across disconnected spreadsheets, paper forms, or outdated software. The previous version of Alveodent, built with Flutter and PocketBase, had reached its limits. A ground-up rebuild was needed: a modern, browser-based SaaS that could serve multiple clinics from a single platform with proper data isolation.
Our Approach
We rebuilt Alveodent as a multi-tenant SaaS with a Turborepo monorepo:
- 4 Packages — Express 5 REST API, React 19 clinic app (Vite + Zustand), marketing landing page, and shared database/types packages
- Row-Level Security — Prisma Extension automatically filters every query by
tenantId, making data leaks structurally impossible - Granular RBAC — 6 hierarchical roles (Super Admin → Owner → Admin → Clinic Admin → Doctor → Staff) with 41 individual permissions
- Full i18n — Spanish, English, and Arabic (with complete RTL support) across the app and PDF generation
Interactive Digital Odontogram
The centerpiece of the clinical module is a fully interactive SVG odontogram:
- Complete dentition — Permanent and temporary teeth with FDI notation
- 9 clinical states — Caries, crown, implant, extraction, bridge, endodontics, and more
- Visual overlay — Temporary teeth rendered at 65% scale over permanent teeth
- Click-to-edit — Dentists update tooth status directly on the diagram
- Tooltips — Hover for detailed state information
- PDF export — Odontogram rendered in clinical history PDFs
Multi-Tenancy with Granular RBAC
Data isolation and access control are enforced at every layer:
Backend:
- Prisma Extension intercepts all queries and injects
tenantIdfilter automatically requirePermission()middleware guards every endpoint- JWT auth with short-lived access tokens (15min) and refresh token rotation (7d)
Frontend:
<Can permission="patients:create">component for declarative UI gating- Zustand store with role-aware state management
- Route guards based on permission sets
Role hierarchy:
| Role | Scope | Example Permissions |
|---|---|---|
| Super Admin | All tenants | Manage all clinics, impersonate |
| Owner | Own clinic | Billing, all clinic operations |
| Admin | Own clinic | Manage staff, view financials |
| Clinic Admin | Own clinic | Manage patients, appointments |
| Doctor | Assigned patients | Clinical records, odontogram |
| Staff | Limited | View appointments, basic data |
Technical Highlights
- FIFO Payment Tracking — Patient payments automatically allocated to oldest outstanding treatments first
- PDF Generation — 4 templates (clinical history, receipts, treatment plans, odontogram) using @react-pdf/renderer with multi-language support
- Transactional Emails — React Email templates via Resend API (welcome, password recovery)
- Real-Time Dashboard — Clinic metrics (revenue, appointments, patient flow) with Zustand-powered reactive updates
- Lab Work Tracking — Full lifecycle management of dental laboratory orders and expenses
- Docker Multi-Stage Builds — Optimized production images deployed on Coolify with Traefik for automatic SSL
Architecture
alveodent/
├── apps/
│ ├── api/ # Express 5 REST API (Node.js 22)
│ ├── app/ # React 19 clinic app (Vite + Zustand)
│ └── web/ # Marketing landing page
├── packages/
│ ├── database/ # Prisma schemas + 12 migrations
│ └── shared/ # Types, constants, utilities
The Results
Alveodent is live in production:
- 1,364 passing tests — 442 backend (Vitest + Supertest) + 922 frontend (React Testing Library)
- 15 DB models + 5 enums across 12 migrations
- 3 subscription plans — Free, Basic ($U 399/month), Enterprise ($U 699/month)
- Complete migration from Flutter/PocketBase across 15 development phases
- ~90K lines of TypeScript/TSX
- Production infrastructure — Coolify + Docker + Traefik with automatic SSL