Skip to content
← archives

FHIR is harder than it looks

#healthcare#FHIR#HL7#backend

When I first started working with FHIR, I assumed the hard part was learning the spec. The spec is 3,000+ pages, so that seemed reasonable. I was wrong. The spec is the easy part.

What FHIR actually promises

FHIR (Fast Healthcare Interoperability Resources) is HL7’s attempt to make healthcare data exchange as straightforward as a REST API. Resources like Patient, Observation, Encounter, and MedicationRequest map cleanly to real-world clinical concepts. The API is RESTful. The data is JSON. It looked, from the outside, like someone had finally fixed healthcare IT.

Then I tried to use it in production.

The conformance gap

FHIR is a framework, not a protocol. Every EHR vendor implements a profile of FHIR — a constrained, extended, sometimes unrecognizable subset of the base spec. Epic’s FHIR API and Cerner’s FHIR API are both “FHIR R4.” They share almost no implementation details.

The Observation resource has about 30 fields. Epic requires 4 of them, ignores 20, and adds 6 proprietary extensions. Cerner requires a different 4, ignores a different 20, and adds different extensions. Your integration code ends up with more if vendor == 'epic' branches than actual business logic.

Auth is its own project

Connecting to a FHIR endpoint requires SMART on FHIR OAuth 2.0. In theory, standardized. In practice: each vendor has slightly different scopes, slightly different token lifetimes, slightly different refresh behavior. Epic requires a backend service app registration that takes 2–4 weeks to approve. Cerner has a sandbox that doesn’t behave like production.

Budget a week just for authentication, minimum.

What actually works

Despite all this, FHIR is still better than what came before it (which was raw HL7 v2 over MLLP — a pipe-delimited format from 1987 that makes FHIR look elegant by comparison).

The trick is to treat each vendor integration as its own adapter. Define an internal canonical data model, write a translator per vendor, and keep the vendor-specific weirdness contained. Don’t let Epic’s extension[0].valueCodeableConcept leak into your business logic.

It’s more work upfront. It’s much less work over the lifetime of the system.


Healthcare interoperability is a solved problem in the same way that “just use REST” is a solved problem for API design. The principles are sound. The details will ruin your week.