---
title: Migraine Tracker
description: >-
  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.
lang: en
source: https://jimrarras.com/en/projects/migraine-tracker/
fetched: '2026-08-24T14:13:24.195Z'
---

1. [Home](https://jimrarras.com/en/)
2. [Projects](https://jimrarras.com/en/projects/)
3. Migraine Tracker

![Migraine Tracker](https://jimrarras.com/images/projects/migraine-tracker/hero.webp)

## 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/sync` endpoint, 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)

- 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
