Skip to content
FHIR for backend developers who were just told "we need FHIR"
article4 min read643 viewsJul 30, 2026

FHIR for backend developers who were just told "we need FHIR"

Resources, references, profiles, and the three things that will surprise you in the first week.

Hannah Weiss@hannahweiss

July 30, 2026 · 4 min read

#1 in Vitals and Variables

643 views6 comments

Since three years I spend most of my working time between hospital systems, and the most common message I get from other developers is some version of: "the customer says we need FHIR, what is it and how bad is it?" So here is the introduction I wish someone had given me. It is not complete. It is the part you need in the first two weeks.

What FHIR actually is

FHIR (Fast Healthcare Interoperability Resources, pronounced "fire") is a standard from HL7 for exchanging healthcare data. In practice it is three things together:

  • a data model: around 150 resource types like Patient, Encounter, Observation, MedicationRequest, Condition
  • a REST API convention: GET [base]/Patient/123, POST [base]/Observation, search with query parameters
  • a way to constrain the model for a country or a use case, called profiles

If you have built a JSON REST API before, the first contact feels friendly. That is intended, and it is also a little bit a trap.

Resources are small on purpose

A blood pressure measurement is not a field on the patient. It is an Observation with a code (what was measured, usually a LOINC code), a subject (a reference to the Patient), an effectiveDateTime and two components for systolic and diastolic. A lab result is an Observation too. So is a body weight. You will have many small resources pointing at each other, not one big document.

{
  "resourceType": "Observation",
  "status": "final",
  "code": { "coding": [{ "system": "http://loinc.org", "code": "85354-9" }] },
  "subject": { "reference": "Patient/123" },
  "effectiveDateTime": "2026-07-21T08:15:00+02:00",
  "component": [
    { "code": { "coding": [{ "system": "http://loinc.org", "code": "8480-6" }] },
      "valueQuantity": { "value": 128, "unit": "mmHg" } },
    { "code": { "coding": [{ "system": "http://loinc.org", "code": "8462-4" }] },
      "valueQuantity": { "value": 84, "unit": "mmHg" } }
  ]
}

References, not joins

Resources link to each other with reference strings like "Patient/123". There are no joins for you on the server side, but two search features replace most of them: _include (give me the Observations and include the Patients they reference) and _revinclude (give me the Patient and everything of type X that points to it). Learn these early. Otherwise you write N+1 calls against a hospital server that has a rate limit and a very tired administrator.

Search results come back as a Bundle of type searchset, paginated with a next link. Always follow the next link and do not build page URLs yourself, servers are allowed to use opaque cursors.

Profiles: the part that surprises everybody

Base FHIR is deliberately loose. Almost everything is optional and many elements allow several data types (value[x] can be a Quantity, a string, a CodeableConcept and more). Real projects therefore use profiles which say "in this country, a Patient must have an identifier of this system and a birth date". In the US that is US Core, for German hospitals it is ISiK, and almost every national program has its own. When a customer says "we need FHIR", the correct answer is "which profiles, which FHIR version, and which interactions". Most of the work hides in that answer.

Validate against the profiles in your CI. The official HL7 validator is a Java jar, it is slow and it is honest. Run it on your example payloads on every build and you will catch most problems before a hospital IT department catches them for you.

Three things that will surprise you in the first week

  1. Versions are not compatible. R4 is what most production systems speak today. R4B and R5 exist and change some resources in ways that matter. Ask, and write the version in your configuration, not in your head.
  2. Codes are everything. The same lab value can arrive with a local code, a LOINC code, or both. Never compare display texts. Build a small terminology mapping table from the beginning, it will grow anyway.
  3. Extensions are normal. Anything the base model does not cover comes as an extension with a URL as its identifier. Your parser must keep unknown extensions and not drop them silently, otherwise you lose data on a round trip and nobody notices until a clinician does.

What I would do on day one

Start a FHIR test server locally (HAPI FHIR runs in Docker in a few minutes), load synthetic patients (Synthea generates realistic fake records, never use real ones for testing), and write the five searches your feature needs. Then read the profile for your country with a coffee. It is dry reading. It saves weeks.

If there is interest I can write about HL7 v2 next, which is older, uglier, and still carries most of the messages inside a typical hospital.

Written by

Hannah Weiss

Clinical informatics engineer in Berlin. I build integrations between hospital systems: HL7v2, FHIR, and a lot of patience. Formerly a nurse, which explains my opinions about alert fatigue.

Get the best of #vitals_and_variables weekly

One email each Monday with the most useful posts. No spam; unsubscribe in one click.

Comments

6

Sign in to join the conversation.

Sign in to comment
Arjun Mehta@arjunmehta#3 in Vitals and Variables
This is very useful, thank you. In fintech we have ISO 20022 and it has the same feeling, base standard is huge and every bank publishes its own "usage guidelines" on top. So profiles = usage guidelines basically?
2 net votes
Hannah Weiss@hannahweiss#1 in Vitals and Variables
Yes, that is a very good comparison. A profile is exactly a usage guideline, only machine-readable, so a validator can check it for you.
1 net votes
Tunde Adebayo@tunde_ade#8 in Vitals and Variables
omo 150 resource types. and i was complaining about our 12 endpoints last week. bookmarking this for the day a hospital client calls us
0 net votes
Mariana Costa@mari_costa#3 in Vitals and Variables
I have a doubt: is there some guideline for showing this data to the patient in an app? Like the units and codes, I suppose we never show the LOINC code to them
4 net votes
Hannah Weiss@hannahweiss#1 in Vitals and Variables
Never show the code. Show the display text from your terminology service in the user's language and keep the code underneath. For patient facing apps also check readability, "HbA1c" means nothing to most people. "Long-term blood sugar" does.
2 net votes
Kevin Tran@kevtran_dev#3 in Vitals and Variables
found this from the search bar like 2 months late lol. our bootcamp final project was a "health app" and we had a column called blood_pressure as a string. "120/80". i see now why that was bad
3 net votes