Migraine Tracker
Lead Developer
A migraine-tracking mobile app where the FHIR export is actually compliant: a neurologist can import the patient's history straight into their EHR. iOS and Android, works offline.
The system
Migraine Tracker is a mobile health app where the FHIR export is actually compliant, meaning a neurologist receiving the file can import the patient’s migraine history straight into their EHR rather than re-typing it from screenshots. React Native 0.81 + Expo 54 on the device, Express 5 + PostgreSQL + Drizzle + Better Auth on the server, expo-sqlite for offline storage. The FHIR layer implements real Observation, Patient, and MedicationStatement resources with SNOMED codes and HL7 terminology, verified against the 4.0.1 specification, not ad-hoc JSON dressed up to look like FHIR. Privacy is wired in: HealthKit entitlements on iOS, Android Health Connect on Android, a privacy policy screen, and a GDPR data export endpoint. Offline-capable via an expo-sqlite local DB with bidirectional sync (a /api/sync endpoint mirrors the server schema onto the device). 99 commits, 16 test suites (717 LOC), Sentry error tracking active on both frontend and backend. Pre-launch, pending final App Store + Play Store assets.
Architecture
- Mobile app: React Native 0.81 + Expo 54, expo-sqlite for local storage, AsyncStorage for session
- Backend: Express 5 + PostgreSQL + Drizzle ORM, Better Auth, 4 dedicated FHIR routes with auth middleware
- FHIR (verified from code, not marketing): Observation, Patient, MedicationStatement resources with SNOMED codes + HL7 terminology systems
- Health integrations: HealthKit entitlements (
health-records) for iOS, Android Health Connect permissions for Android - Privacy: Privacy policy screen, GDPR data export endpoint
- Offline sync: Bidirectional
/api/syncendpoint, expo-sqlite mirrors backend schema - Observability: Sentry on frontend + backend
- Tests: 16 test suites (717 LOC) with Jest + supertest for backend, native builds for Android + iOS
My contribution
I built the entire stack on my own: mobile app, backend, FHIR layer, offline sync. React Native + Expo was a deliberate choice for one codebase across iOS and Android plus OTA updates without going through app-store review. On the backend I wrote separate FHIR routes with dedicated middleware because clinical exports follow a different auth flow than regular API calls (a doctor receiving an export is not necessarily the user who created the data). I verified the FHIR output against the 4.0.1 spec byte by byte: Observation resources have the right resourceType, status, SNOMED code.coding, subject reference, and valueQuantity. The offline sync uses last-write-wins semantics, which is the right call for single-user personal health data (simple, predictable, no conflict-resolution UI). The 16 test suites cover FHIR routes, sync logic, and auth middleware.
Stack details
The verified FHIR implementation is the most noteworthy technical element. Many health apps claim FHIR compliance, here the code is verifiable: Observation.code.coding contains the SNOMED CT system URL (http://snomed.info/sct) with a corresponding code, MedicationStatement.medicationCodeableConcept uses HL7 terminology. The expo-sqlite bidirectional sync solves the classic problem: the user logs a migraine offline on the train, the app stores it locally, and when back on a network the /api/sync endpoint merges it. The HealthKit entitlements for health-records allow reading existing medical records from the iOS Health app, the user can import medication history instead of typing it in.
Outcomes
- Verified FHIR 4.0.1 compliance: not a marketing claim, real code with SNOMED + HL7
- Offline-first sync mirroring backend schema
- Privacy-first design with GDPR data export
- Ready for App Store / Play Store submission (pending Sentry DSN + final asset polish)
The challenge
The most demanding part was modelling migraine episodes as FHIR Observation resources. A migraine isn’t a simple measurement (e.g. heart rate), it’s an episodic event with onset, duration, severity, triggers, and associated medications. In FHIR 4.0.1, the Observation resource is designed primarily for measurements. I had to decide: I use valueQuantity for severity (0–10 scale), a component array for triggers, and separate MedicationStatement resources for medications taken. Onset/duration goes into effectivePeriod (start + end timestamps). For triggers I used component.code.coding with custom SNOMED codes for common triggers (stress, sleep, caffeine). The result is valid FHIR that a doctor can import into their EHR system, that was the goal.
Links
- Repo:
jimrarras/migraine-app(private)
Related work
- Zosimades Healthcare Ops Stack: other healthcare software, but institutional ops rather than personal health
- collection-app (part of the Zosimades stack): another offline-first mobile app
Gallery