Migraine Tracker
Lead Developer
A migraine-tracking mobile app whose export is real FHIR 4.0.1: attacks modelled as Observations coded in SNOMED CT and HL7 terminology, not ad-hoc JSON dressed up to look like it. iOS and Android, works offline.
The ten-minute appointment
Anyone living with migraine keeps notes: in a notebook, in the phone’s notes app, in screenshots. At the neurologist’s appointment all of that becomes a spoken summary, and the doctor re-types whatever fits. I built an app that records the attack while it is happening and produces a file the doctor’s system reads on its own.
Logging while the attack is still on
Logging happens with the pain still going, so it has to be short: intensity, where in the head, symptoms, what came before it, what was taken. A timer keeps the attack open and closes it when it passes.
Nothing waits for a network. The entry is written on the device first and synced afterwards, because an attack does not wait for you to find signal.

How it works
- What an entry holds: intensity 0 to 10, start and end time, duration type, pain sites on a head map, symptoms with a severity, triggers, medications with a dose and whether they helped, and free notes.
- Local first: a local SQLite database with a write queue, so the screen never waits on the server.
- Sync: a bidirectional
/api/syncendpoint with last-write-wins, the right call for one person’s own data: simple, with no conflict-resolution screen. - Deletes: removed entries travel as tombstones, so a delete does not reappear from another device.
- Backend: Express 5 with Drizzle ORM over PostgreSQL, with Better Auth.
Patterns, not impressions
After a few weeks the app starts answering questions nobody remembers accurately: how often, how badly, what tends to come first, which medication actually works.
The weather is recorded with every attack, so a change in barometric pressure is a measurement rather than a feeling.

How it works
- Weather: temperature, humidity, condition and barometric pressure are stored with each entry, along with the pressure change over the previous 24 hours.
- Analysis: frequency per period, trends, sensitivity per trigger and a risk reading for the day, all computed from the entries themselves.
- Medications: doses, reminders and a taken-history per medication.
- Checks: 793 automated tests across 115 files cover the calculations, the sync and the screens.
A file the doctor’s system can read

Most health apps hand over a PDF. Here there is a second road: the history comes out as FHIR, the language hospital systems already speak, with international medical codes instead of free text. The doctor imports it.
How it works
- What is exposed: a REST API publishing a CapabilityStatement with
fhirVersion4.0.1, serving Patient, Observation (read and search by date,_count,_sort) and MedicationStatement asapplication/fhir+json, with errors as OperationOutcome. - An attack as an Observation:
statusfinal, categorysurveyfrom the HL7 terminology,codeset to SNOMED CT 37796009 (Migraine), asubjectreference to the Patient, andeffectivePeriodfor start and end. - Intensity: carried as
valueIntegeron a 0 to 10 scale, withinterpretationfrom the HL7 v3-ObservationInterpretation system. - Sites and symptoms: 8 SNOMED codes for head regions in
bodySiteand 6 for symptoms as components, each with a severity. - Duration and triggers: components under SNOMED codes 103335007 and 134296009, with the trigger itself as a text value.
- Access: the endpoint accepts either the patient’s session or a Patient Access Token, stored only as a SHA-256 hash, with a label, a scope, an expiry and a revocation.
The data stays the patient’s
A pain diary is one of the most personal records a person keeps. Nothing leaves without an explicit action, and the user can take all of it with them or delete it.
How it works
- Export and deletion: a GDPR endpoint returning everything on the account, and account deletion behind a confirmation.
- Device health: the
health-recordsHealthKit entitlement on iOS and Android Health Connect through an in-house expo plugin, so history that already exists does not have to be typed again. - Lock: an app-lock overlay when the app goes to the background.
- Error tracking: Sentry on both the app and the backend.
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