00-project-overview.md

Patient Tracker System — Project Overview

What It Does

A full-stack healthcare platform where doctors manage patients and patients track their treatment. Two user roles, one shared data layer.

Feature List

FeatureWho Uses ItWhat It Does
Role-based AuthBothSeparate signup/signin flows for doctors and patients. JWT-based session. Doctors can invite patients via email.
Patient ManagementDoctorAdd patients (creates pending account + sends email invite), assign existing patients, remove patients from roster.
Prescription SystemDoctorCreate prescriptions with multiple medicines, each with dosage, timing (morning/afternoon/evening), instructions, and duration. Delete prescriptions with cascading cleanup.
Medication TrackingPatientDaily view of scheduled medications grouped by time-of-day. Mark as Taken/Missed. Adherence stats and history.
Automated RemindersSystemCron-based email reminders at 8 AM, 1 PM, 8 PM. Auto-marks medications as Missed after their time window expires.
Appointment BookingPatientBook 1-hour slots (9 AM – 5 PM) with assigned doctors. Concurrency-safe via partial unique DB index.
Appointment ManagementBothDoctors: complete/cancel/mark missed. Patients: cancel only. Filter by date/status.
Real-time ChatBothSocket.IO-powered messaging between doctor and assigned patient. Read receipts. Chat history.
Medicine DatabaseSystem32 MB CSV dataset of Indian medicines parsed on-demand for prescription autocomplete.
Password RecoveryBothForgot password → email with time-limited reset token → set new password.

Why This Project Exists

Clinics — especially small ones — still manage patient medications, reminders, and appointments through phone calls, WhatsApp, or paper. This system digitizes the doctor ↔ patient relationship end-to-end:

  • Doctor adds a patient → patient gets an email → sets password → immediately sees prescriptions and medication schedule.
  • Patient doesn't need to remember what to take when — the system generates a daily medication schedule from active prescriptions and emails reminders.
  • Appointments are booked without phone tag — patient picks from available slots, double-booking is prevented at the DB level.
  • Doctor can monitor which patients are actually taking their meds via adherence stats.

Real-world analog: Think of it as a self-hosted version of Practo or MyChart, scoped to the prescription-and-adherence workflow.


High-Level Architecture

┌─────────────────────────────────────────────────────────────────────┐ │ BROWSER (React 19 + Vite) │ │ ┌──────────┐ ┌──────────────┐ ┌────────────┐ ┌──────────────────┐ │ │ │ Signin │ │ Doctor Dash │ │Patient Dash│ │ Chat Interface │ │ │ │ Signup │ │ - Patients │ │ - Meds │ │ (Socket.IO) │ │ │ │ SetPwd │ │ - Prescribe │ │ - Rx View │ │ │ │ │ │ ResetPwd│ │ - Appts │ │ - Appts │ │ │ │ │ └──────────┘ └──────────────┘ └────────────┘ └──────────────────┘ │ │ │ │ │ │ │ │ └──────────────┴──────────────┴───────────────┘ │ │ │ axios (HTTP) │ socket.io-client │ └────────────────────────────┼─────────────────┼─────────────────────┘ │ │ ▼ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ EXPRESS SERVER (Node.js, port 8000) │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐│ │ │ Middleware Pipeline ││ │ │ express.json() → cors() → triggerMedicationReminder() ││ │ │ → checkForMissedMedications() → [route handler] ││ │ └─────────────────────────────────────────────────────────────────┘│ │ │ │ ┌──────────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐ │ │ │ auth/ │ │ doctor/ │ │ patient/ │ │ chat/ │ │ │ │ signup │ │ addPat │ │ meds │ │ chatCtrl │ │ │ │ signin │ │ prescRx │ │ prescRx │ │ msgCtrl │ │ │ │ resetPwd │ │ assignPat│ │ adherence │ └──────────┘ │ │ │ forgotPwd│ │ removePat│ │ history │ │ │ │ setPwd │ │ getMeds │ │ stats │ ┌──────────────┐ │ │ └──────────┘ │ getPresc │ └────────────┘ │ appointment/ │ │ │ └──────────┘ │ create │ │ │ │ getSlots │ │ │ ┌───────────────────┐ │ updateStatus │ │ │ │ scheduleTasks.js │ │ getDr/Pt │ │ │ │ (node-cron) │ └──────────────┘ │ │ │ 8AM/1PM/8PM │ │ │ │ + every-minute │ ┌───────────────────┐ │ │ │ missed checker │ │ Socket.IO Server │ │ │ └───────────────────┘ │ (real-time chat + │ │ │ │ med notifications)│ │ │ └───────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ Prisma ORM Client + Accelerate Extension │ │ │ └─────────────────────────────┬────────────────────────────────┘ │ └────────────────────────────────┼─────────────────────────────────┘ │ ▼ ┌───────────────────────┐ │ PostgreSQL (Neon) │ │ via Prisma Accelerate│ │ │ │ Tables: │ │ Patient, Doctor, │ │ Prescription, │ │ PrescribedMedicine, │ │ MedicineAdherence, │ │ Appointment, │ │ Chat, Message, │ │ MedicalRecord, │ │ HealthMetrics │ └───────────────────────┘

How Layers Connect

Browser (React) │ ├─ HTTP (axios) ──────────→ Express Routes │ │ │ ├─ authenticate() middleware verifies JWT │ │ │ ├─ Route handler (e.g., doctor/prescription.js) │ │ │ │ │ ├─ Validates input │ │ ├─ Checks authorization (role + ownership) │ │ ├─ Calls Prisma ORM │ │ │ └─ PostgreSQL query │ │ └─ Returns JSON response │ │ │ └─ Global middleware on EVERY request: │ ├─ triggerMedicationReminder() ← fires emails asynchronously │ └─ checkForMissedMedications() ← auto-marks missed meds │ └─ WebSocket (socket.io) ──→ Socket.IO Server ├─ join-chat / leave-chat (room management) ├─ send-message → broadcast to room └─ medications-updated ← server pushes to patients

No cache layer, no queue, no CDN. Everything is synchronous request → DB → response, except:

  • Email sending (fire-and-forget via Nodemailer)
  • Cron jobs (background medication checks)
  • WebSocket events (real-time chat + medication update notifications)

Key Design Decisions and Trade-offs

DecisionRationaleTrade-off
Modular file-per-handler (no router files)Each handler is self-contained — easy to read in isolationapp.js becomes a 186-line import-and-route registry
JWT with all user data in payloadNo DB lookup needed for auth check — pure statelessToken bloat; changing user data requires re-login to get new token
Medication reminders fire on every HTTP requestEnsures reminders trigger even if cron fails (Vercel kills long-running processes)Adds ~0ms overhead per request (async fire-and-forget), but it's unconventional
Prisma AccelerateConnection pooling for serverless (Vercel) where direct Postgres connections would exhaust poolAdds latency (~5-15ms) per query vs direct connection
CSV medicine dataset loaded on-demandAvoids 32MB memory resident data; streaming parse via csv-parserEvery /api/medicines call re-reads the file — no caching
Partial unique index for appointment concurrencyDB-level guarantee — no double-booking even under concurrent insertsRequires raw SQL migration outside Prisma's declarative schema
Socket.IO on global ioQuick way to share the socket instance across middleware and cron jobsGlobal mutable state; not testable in isolation
No separate router layerRoutes defined inline in app.jsNo route grouping, middleware reuse, or prefix-based organization

What I Would Do Differently If Starting Over

  1. Use Express Router with grouped route files — one router per domain (auth, doctor, patient, appointment, chat) instead of 30+ individual imports in app.js.

  2. Extract business logic from controllers — current handlers mix validation, authorization, business logic, and DB queries in one function. A service layer would make each testable independently.

  3. Don't fire medication reminders on every HTTP request — the middleware approach in app.js line 74-77 is a workaround for serverless cold starts. A proper solution: use a dedicated cron service (Vercel Cron, Railway, or a standalone worker).

  4. Cache the medicine CSV — 32MB re-parsed on every search request is wasteful. Load once on startup, or import into a DB table.

  5. Use refresh tokens — current JWT expires in 24h with no refresh mechanism. User gets silently logged out.

  6. Add input validation library — current validation is manual if (!field) checks. Zod or Joi would make schemas declarative and consistent.

  7. Separate WebSocket concerns — chat and medication notifications share the same Socket.IO instance with no namespace separation. At scale, these should be different namespaces or even different services.

  8. Add proper error types — current error handling returns inconsistent shapes ({ message } vs { error } vs plain strings). A custom AppError class with status codes would standardize this.