OOOlorede.
Back to Projects
Full-StackFeaturedMarch 2025

DriftCare NG

AI-powered health monitoring platform that detects subtle wellness drift before it becomes a crisis.

React 19ViteTypeScriptExpressMongoDBMongooseTanStack React QueryTailwind CSS 4Radix UIFramer MotionRechartsOpenRouterGoogle Gemini 2.5 FlashJWTCloudinaryWeb Push (VAPID)RedisSwagger / OpenAPIVercel
DriftCare NG
Overview

DriftCare NG is a fullstack health intelligence platform built for Nigerian users that tracks daily wellbeing across 8 clinical dimensions (sleep, stress, mood, activity, hydration, symptom load, health status, and lifestyle) and computes a real-time 'drift score' — the percentage deviation from each user's personal baseline. Rather than comparing against population averages, the system establishes an individualised baseline from the user's first 10 check-ins, then flags deterioration trends early. An AI health companion, context-aware and culturally localised for Nigeria, delivers insights grounded in the user's actual drift data. Clinical outputs in HL7 FHIR R4 and SBAR format allow seamless handoff to medical professionals, making the platform EMR-integration ready.

Architecture

The frontend is a React 19 SPA served from Vercel's CDN. TanStack React Query owns all server state with stale-while-revalidate semantics. Auth is handled via JWT stored exclusively in HTTP-only cookies (access token: 1h, refresh token: 7d), with a non-httpOnly hint cookie letting React Router guards make synchronous auth decisions without exposing the real token to JavaScript. On 401, an Axios interceptor silently hits the refresh endpoint and retries the original request. API traffic routes to an Express backend deployed as Vercel Functions under /api/v1/. Each feature domain (user, check-in, dashboard, chat, doctor, media, task) is fully modular: its own controller, service, route, Mongoose model, DTO, validator, and entity. The AI chat endpoint builds a context window from the user's recent and baseline check-in cohorts, injects the computed drift percentage into the system prompt, and forwards the conversation to OpenRouter (Gemini 2.5 Flash) via a pluggable IAIProvider abstraction.

Technical Decisions
01

Auth Security

Chose

HTTP-only cookies for JWT over localStorage

Why

localStorage is fully accessible to any JavaScript running on the page, making stored tokens trivially exfiltrable via XSS. HTTP-only cookies are inaccessible to JS by spec. A secondary non-httpOnly boolean hint cookie allows React Router guards to make synchronous auth decisions on first render without exposing the token.

Trade-off

Requires CORS credentials (withCredentials: true) on every request and explicit SameSite configuration. Also complicates cross-subdomain auth if the client and API ever live on different origins.

02

AI Provider

Chose

OpenRouter with Google Gemini 2.5 Flash over direct OpenAI GPT-4o

Why

Gemini 2.5 Flash provides near-GPT-4 quality at significantly lower cost-per-token, which matters for a health app where every dashboard load and chat message triggers an inference call. OpenRouter as the abstraction layer keeps the AI provider swappable without touching the chat service.

Trade-off

Adds a network hop through OpenRouter's proxy. Output token limits were intentionally capped (300 for chat, 500 for structured extraction) to constrain latency and cost, which limits response depth for complex health questions.

03

AI Architecture

Chose

Pluggable AI provider pattern (IAIProvider interface + AIService registry)

Why

Locking the chat service to a single SDK import makes model migration a refactor. The provider pattern means adding Anthropic or a locally-hosted model is a new file, not a modification to existing service logic.

Trade-off

Introduces an abstraction layer for a system that currently has only one active provider, adding indirection when tracing an AI call.

04

Database

Chose

MongoDB over a relational database (PostgreSQL)

Why

Health check-in data is highly variable: symptom arrays, lifestyle enums, and medical report lists differ per user and evolve as the product adds dimensions. A document model avoids schema migrations during fast MVP iteration. Mongoose's embedded document support collapses the 5-step check-in form into one atomic write.

Trade-off

No joins — dashboard aggregations require multiple round-trips or $lookup pipelines. Referential integrity between User and DailyCheckIn documents is application-level responsibility.

05

Clinical Standards

Chose

HL7 FHIR R4 and SBAR as clinical output formats

Why

Producing proprietary JSON blobs would limit the platform to consumers who custom-integrate with it. FHIR R4 is the mandated interoperability standard across Nigerian and international healthcare systems. SBAR is the clinical communication standard used by nurses and physicians for handoff.

Trade-off

Full FHIR compliance is non-trivial. The current implementation maps sleep duration to LOINC code 8967-7 and uses a placeholder 85354-9 for the full health panel — integration-ready but not yet fully coded.

06

Platform Strategy

Chose

PWA over a native mobile app (React Native / Flutter)

Why

A PWA is installable on iOS and Android directly from the browser with no App Store submission cycle. For a hackathon build targeting rapid user validation in Nigeria, reducing install friction and eliminating the store review delay was the dominant constraint.

Trade-off

PWA push notifications on iOS are gated behind iOS 16.4+ and have lower reliability than native push channels. Background sync and certain device API access remain limited.

07

Drift Algorithm

Chose

Baseline-relative drift detection over population-average comparison

Why

A user who chronically sleeps 5 hours is not the same as one who has recently dropped from 8 to 5. Using each user's own first 10 check-ins as a personalised baseline makes the drift signal clinically meaningful — it detects change, not deviation from a generic healthy norm.

Trade-off

The model requires a minimum of 10 check-ins before baseline stabilises. New users see no drift data during onboarding, creating a dead period for the core feature.

Future Improvements

Migrate cron jobs (daily reset, push reminders) to Vercel Cron — the current node-cron setup breaks in Vercel's stateless serverless environment

Fully wire Redis for API response caching and per-user rate limiting on the AI chat endpoint, which is currently unbounded

Expand the drift algorithm from 3 dimensions (sleep, stress, mood) to all 8 scored dimensions for a more complete drift signal

Complete FHIR LOINC coding — replace the placeholder code 85354-9 with correct LOINC codes per metric for true clinical compliance

Persist chat conversation history per user session to MongoDB so the AI companion has multi-turn context across sessions