How J4H connects to clinical health records — the standard it uses, how the connection is made, the code that powers it, and why the strategy matters.
FHIR stands for Fast Healthcare Interoperability Resources. It is a standard published by HL7 (Health Level Seven International) that defines how health data should be structured and shared between systems. J4H uses the R4 version — the current stable release.
Every search returns a Bundle — a wrapper containing a list of matching resources.
Each item in bundle["entry"] holds one resource.
A personal health diary captures what the patient experiences — pain level, symptoms, location, context. A FHIR server captures what the clinician records — diagnoses, lab values, measurements. J4H connects the two.
fhir_service.py
Four FHIR servers are pre-configured as a dict. The one actually used is
selected by the FHIR_SERVER
environment variable. On Heroku this is set to hapi-gtri.
Locally it defaults to the public HAPI sandbox.
All HTTP communication lives in one class. The constructor sets the base URL
and the required FHIR content-type headers. Every method builds a URL, fires
a requests.get() call with a 10-second timeout, then parses the response.
Vitals and cholesterol live in different Observation categories on FHIR servers,
so get_patient_vitals() fires two HTTP requests and merges the results.
Blood pressure is special: it stores systolic and diastolic as sub-components
within one Observation resource, so the parser loops over resource["component"].
At the top of app.py a single
FHIRService object is created when the server starts.
Every route handler reuses it — no reconnection overhead on each request.
| Method | Route | What it does |
|---|---|---|
| GET | /api/fhir/patients/search | Search patients by name — calls fhir.search_patients() |
| GET | /api/fhir/patient/<id> | Full patient record: demographics + conditions + observations |
| POST | /api/fhir/patient/select | Stores chosen patient_id + name in the local settings table |
| GET | /api/fhir/patient/current | Returns the currently selected patient (used by the SPA on load) |
| GET | /api/fhir/vitals | Grouped vitals dict for the selected patient — powers the Vitals page charts |
| GET | /api/fhir/health-record | Combined patient + conditions + observations for the Health Record page |
| GET | /api/fhir/server/status | Tests connectivity to the configured FHIR server (used by Admin dashboard) |
When the user selects a patient on the Patient Data page, the frontend POSTs to
/api/fhir/patient/select.
Flask verifies the patient exists on the FHIR server, then writes two rows
to the local settings table.
Every subsequent FHIR call reads from there — no session cookies needed.
Raw FHIR JSON is deeply nested and full of optional fields.
Each _parse_*() method extracts what J4H needs and returns a flat dict.
The rest of the app never touches raw FHIR JSON.
code.text, sometimes only code.coding[0].display.
The parser handles both cases once. The rest of the app just reads
condition["code"] — a plain string.
GET /api/fhir/patients/search?name=Smith.
Flask calls fhir.search_patients(name="Smith"), which hits
GET {base_url}/Patient?name=Smith&_count=20.
The FHIR server returns a Bundle; Flask parses and returns a list of patient dicts.
{ patient_id: "pat-001" } to
/api/fhir/patient/select.
Flask verifies the patient exists, then stores
selected_patient_id = "pat-001" and
selected_patient_name = "Jane Smith" in the local settings table.
loadCurrentPatient() calls
GET /api/fhir/patient/current.
Flask reads selected_patient_id from the settings table,
fetches the patient from the FHIR server, and returns it.
The SPA sets the global currentPatientName shown in the header.
GET /api/fhir/vitals.
Flask reads selected_patient_id from settings and calls
fhir.get_patient_vitals(patient_id) — two HTTP calls
(vital-signs + laboratory) that return a grouped dict like
{ "Systolic BP": [{date, value, unit}, …], … }.
The vitals page renders this as Chart.js line charts.
GET /api/fhir/health-record fetches patient + conditions + observations
in one Flask route and returns them bundled as a single JSON response.
The current integration uses open FHIR servers (GTRI, HAPI public) that require no authentication. Real EHR systems — Epic, Cerner, etc. — require SMART on FHIR, which adds an OAuth 2.0 authorization layer on top of FHIR.
Authorization: Bearer <token>.
FHIR_SERVERS — points to SMART Health IT test launcherFHIR is the plumbing of modern healthcare interoperability.
J4H uses it to do something simple but powerful: place clinical context next to personal experience. When an AI summarizes your diary entries for your cardiologist, it can reference real cholesterol readings and blood pressure trends — not just what you remember to write down.
That's the integration strategy in one sentence: your story, grounded in your data.