FHIR: The important bits

The FHIR standard describes how health related information can be moved around a healthcare ecosystem, describing both information structure and exchange mechanisms. It’s straightforward to learn and use, but can be a bit overwhelming to the newcomer as it’s grown quite large since we first started it over 10 years ago.

This post is intended for people unfamiliar with FHIR, and we’ll take a quick look at the most important things to be familiar with as you start your FHIR journey.

It’s very much a ‘whistle stop’ tour – think of it as a list of things to explore further yourself.

It’s primarily an exchange specification

The primary purpose of FHIR is to be able to exchange information between systems in a way that preserves the meaning of the data – often called semantic interoperability. Although it can be used for storage of data (and many do), this is by no means required. In fact many of the larger systems use a FHIR interface to convert to and from their existing internal storage representations.

There are pro’s and con’s to storing data in the FHIR format. The biggest con is that the specification is still being developed and so you may need to perform conversion when you move to newer versions. Also, the format is not optimized for storage and reporting.

The biggest pro is that there are freely available servers – the HAPI server being an examples, but there are others. 

As well as exchanging data, FHIR also covers other parts of the healthcare domain such as Security, Clinical Reasoning and Workflow. In fact, FHIR has grown to cover most aspects of healthCare information – largely due to the community that has grown up around it.

Community based and freely available

Developed under the auspices of HL7, an international Standards body with deep roots in health (their version 2 standard has been very widely used within Laboratories and hospitals since the 1980s), FHIR has been developed with enormous input from the global community. 

Supporting this is a Zulip based on-line chat where pretty much any question about FHIR can be answered by the community. The code of conduct ensures that newcomers are welcomed – no flaming allowed (despite the standards name 🙂 )

The specification is available here, and can be downloaded if needed. It is free to use, and comments are welcomed.

Still in development

While already widely used globally, FHIR is still being actively developed and there are a number of versions that are available for use. This long development time (over 10 years) has allowed developers to actually use it to ensure that it is fit for purpose, as well as providing plenty of time for community input.

At the time of writing (March 2026) release 4 (R4) is the most widely used and the version you’ll come across most often. We are currently working on release 6 which should be complete by the end of the year and which time the core standard will be considered largely complete, with any changes ensuring that R6 implementations won’t be broken by any subsequent changes.

Resources 

Fundamentally, FHIR models healthcare data as a set of Resources which can be ‘joined’ together using references. In fact, the name FHIR stands for “Fast Healthcare Interoperability Resources’.

A resource represents a ‘thing’ (or entity if you want to be more precise) such as a Patient, Practitioner, Observation or Condition.

There are around 140 resource types defined in R4, each of which has a page in the Spec that describes its purpose, the elements within the resource (such as name & address for a Patient) and other information about it. 

Each resource type has a descriptive name that describes what it represents – usually the name is enough to understand the purpose of a resource type, but there’s a lot more information in the spec.

When you’re sharing FHIR data, you’re basically sharing resources.

Here’s part of the definition for a patient.

Table displaying patient information schema, including fields such as identifier, active status, name, telecom details, gender, birth date, deceased status, address, and marital status.

Each element has characteristics like:

  • its name
  • how often it can appear in a resource (Card. = Cardinality)
  • dataType
  • description

The elements in a resource are defined by the spec. You can add extra ones using extensions which are described under profiling.

Resource types and instances 

When we’re talking about resources, we can be thinking about the design of a resource, or an actual resource with data. We refer to the design aspect as a ‘type’ and the data as an ‘instance’

An analogy is a cookie cutter – used to make cookies of the same shape. In this case the cutter is the type, and an actual cookie is the instance. 

We’ll try to be explicit in this article, but it’s an important distinction to make when thinking about FHIR.

DataTypes

Looking at the image above, you can see that each element  has a datatype (in some cases more than one). This represents the ‘kind’ of information for elements.

DataTypes can be simple or complex

  • Simple ones are just a value – like date or boolean
  • Complex ones have multiple ‘sub-elements’. For example the HumanName dataType has sub elements of family, given or text (among others)

Clicking on a dataType in the spec will display the details of that dataType. In fact, the specification is heavily hyperlinked which makes it easy to explore.

Here’s HumanName:

Diagram showing the structure and constraints of a HumanName element, including attributes such as use, text, family, given, prefix, suffix, and period.

Where an element has more than one dataType – like deceased[x] – then the resource instance (an actual patient) can have either (or none) of them. Both cannot be present in the same instance.

References between resources – the resource graph

A single resource is interesting, but usually not enough in most scenarios. For example, suppose you want to share a patient’s problem list. Looking at the list of resources, it appears that the Condition resource is the appropriate resource type to use, but looking at the definition to see where the patient details are, we see that the subject of the Condition is a reference dataType. 

Table displaying the structure and constraints for the FHIR Condition resource, including fields like identifier, clinical status, verification status, category, severity, code, body site, subject, and encounter.

So instead of including the patient details all over again in each condition, we create a single Patient resource instance, and then reference that resource from the condition resource to create a graph of resources. 

Diagram depicting a patient named John Doe connected to two conditions: Diabetes and Asthma, labeled with 'subject' arrows.

This is probably THE most important thing to get your head around about FHIR – fundamentally it is about resource instances connected together by references. 

If you want to look at more examples, check out the clinFHIR Patient Viewer. To create your own, try the Graph Builder

Representing a resource instance

There are a number of ways of representing a resource (eg if we wanted to store it in a file). The most common way of doing this is using a format called JSON. Here’s an example of a patient if you saved it as a file, then opened it in a text editor:

JSON representation of a patient record including ID, name, gender, and birth date.

There isn’t space here to go into JSON in detail, but some observations:

  • elements are in pairs – name : value
  • the element name from the spec is the same in the Json (eg name, gender)
  • values for elements with complex datatypes are surrounded by {}
  • repeating elements are surrounded by [] (even if there is only one)

XML  is also used, though less frequently.

The Bundle resource

So whenever we’re sharing data in FHIR we’re moving resource instances (with the included references) around. Because there can be a lot of them, generally we place these resource instances inside a special ‘container’ resource – the Bundle resource and then move that around.

Here’s an example bundle:

JSON representation of a patient bundle including personal details like name, gender, and birth date.

clinFHIR also has a bundle viewer that allows you to view the contents of a bundle with a number of views.

Exchange mechanisms

There are a number of ways to share resources between 2 (or more) systems – we call these ‘exchange paradigms’.  As stated earlier, regardless of the mechanism, it is still resources being shared.

The simplest is the RESTful API – which is very similar to how you retrieve a web page. It’s a real-time request/response operation – make a request, and get the answer back immediately. eg ‘give me the patients whose last name is Doe’. The response will be a Bundle of matching Patients (if any – but always a bundle even if there are no matches). It assumes that the server is on-line and available. 

The spec defines the query parameters that could be supported – they are at the bottom of each resource type description. Note that any given server can choose what it will support – there’s no requirement to implement them all. Other query parameters can be defined, and there’s a special resource the server can use to indicate to a client what its capabilities are. There are also options for sorting and filtering.

Operations are an extension of this type of interaction – commonly used to provide a simple API to complex processes – like expanding a ValueSet or creating a Document. The spec defines a number of operations, but you can define your own.

Messaging is where a request message is sent from one system to another when an event happens. For example a hospital ADT system might send a message to the Laboratory system when a patient is admitted so the Lab system has the patient details for when a test is ordered. The message is a Bundle of resources including a MessageHeader resource which has the details of the event. Messaging is not usually real-time and commonly the message passes through other systems like an Integration Engine to manage the delivery. HL7 version 2 messaging is a great example of non-FHIR messaging.

Another paradigm is the Document paradigm where the document is something like a Discharge Summary or Clinic Note. The application creates a Bundle with the required resources including a Composition resource for the ‘header’ information like document type, patient, author and date created. HL7 CDA is an example of documents from an earlier HL7 Standard (and there’s actually a FHIR IG that describes CCDA – a US CDA implementation.)

Terminology interaction

For semantic information exchange it’s necessary to have some terminology system that can define concepts that are recognized by both the sender and recipient of the data. Examples of such systems are SNOMED-CT, LOINC and ICD but there are many others including some defined in the specification and used for ‘structural’ purposes such as resource status.

There’s a section in the spec that deals with terminology, but the key points are as follows.

  • A coded value is referred to as a concept and includes the code, description and system that defines it
  • A coding system (such as SNOMED-CT) is represented in FHIR by a unique url – http://snomed.info/sct in this case. It is expected that each coding system will manage its internal codes ensuring they are unique within the coding system
  • There are 3 dataTypes in FHIR that can hold coded data in resource instances. These are
    • code – used for concepts defined in the specification
    • Coding – refers to a single concept using the system url, and the identifier from the coding system
    • CodeableConcept – can contain multiple Codings (e.g. if you want to represent something in multiple systems like translated data) and can also have a text element.

The most commonly used dataType is CodeableConcept, particularly as it supports text if a code is not available (you lose semantic rigour, but at least a human can read it). It’s also useful when converting a concept between systems as it can contain both original and converted coding.

So to store, say, a SNOMED-CT code in Condition.code the instance will look like this:

JSON representation of a medical condition with details about asthma, including a coding reference.

So that’s how a resource instance refers to a coded value in a terminology. But what if you want to provide a set of concepts for a user to select from, or that are valid for a given element? Say a list of the most common SNOMED-CT codes for an ED diagnosis. For that you use a ValueSet.

A ValueSet is a set of concepts from one or more coding systems. It can be quite complicated to define the set – but its purpose as a set of concepts is straightforward. Once you have the ValueSet, you can then say that the value for a specific element could / should / must come from that ValueSet. This process is called ‘binding’ the ValueSet to the definition of the element.

When specifying a binding (ie a ValueSet of applicable concepts) you also specify the binding strength. This indicates if the value in a given instance MUST (SHALL) be in the ValueSet, SHOULD be in the ValueSet or the ValueSet is just an example. Binding becomes important when profiling – as we’ll see shortly.

Here are some bindings from the Condition resource.

Table displaying the structure and constraints of a medical 'Condition' resource, including fields like identifier, clinical status, verification status, category, severity, code, body site, subject, encounter, and onset.

For each element with a coded dataType we see:

  • A description of the ValueSet, or the contents if there are only a few
  • The ValueSet name – hyperlinked so you can view it in more detail
  • The binding strength – in brackets

It’s very important to appreciate that the ValueSet is used in definition of the resource type (or profile). In the actual instance, the CodeableConcept directly refers to a concept in the terminology – the ValueSet is nowhere to be seen.

Profiling

If you look at any resource type you’ll probably find missing elements, or ones that you don’t need. Adapting resource types to a specific use case is called profiling.

Profiling can be quite complex, but commonly you will want to:

  • add additional elements using extensions. These have the same capabilities as the core elements.
  • specify specific things you want – like a specific kind of identifier
  • change the cardinality so that something optional in the spec becomes required (you can’t make a required optional, or something that is single become repeatable)
  • change a ValueSet binding to a different ValueSet so there are a different set of concepts. Note that you can’t downgrade a binding strength from that in the spec – and if the strength is required, then you can’t change the ValueSet.

The mechanism within FHIR to support profiling is exactly the same as that which defines the core parts of it – both a core resource type and  a profiled resource are represented by the StructureDefinition resource. Note that you cannot create a new resource, you need to choose one of the existing ones – or use the Basic resource type and extend that.

This means that when you create a profile and view it in an Implementation Guide, it will look very similar to the core spec.

Implementation Guides

Implementation guides (IG) are used to describe how to use FHIR in specific situations – and there are a lot of them. There is a searchable registry of IGs as well a list of some of them available from fhir.org

There is specific tooling support for IGs – in fact it is the same tooling used to create the core specification, which means that the guide and the artifacts within it are computable and downloadable – and the IG will appear similar to the base spec, though that can be changed.

Commonly an IG will contain:

  • Textual information(and images)  about the purpose of the guide, description of processes and details of how FHIR artifacts are to be applied. There is a lot of text in an IG – it is intended both for humans and computers
  • Profiled resource types
  • Extensions (additional elements that can be applied to resource types)
  • ValueSets and CodeSystems
  • Additional search parameters
  • Specific Operations
  • Examples. In fact the IG tooling enforces the use of examples.

Here are some examples of IGs that you may come across

  • SMART app launch – securely invoking FHIR based application from other systems
  • IPS (International Patient Summary) – a document based approach to recording common clinical information
  • IPA – key interactions to support patient access to their records
  • Structured Data Capture (SDC) – how to build sophisticated forms
  • Bulk Data – retrieving large amounts of data for analytics and other purposes
  • SQL on FHIR – accessing FHIR based data using SQL
  • Clinical quality measures – measuring clinical quality
  • CDS hooks – hooking Clinical Decision Support into clinician workflow

This post has gone on rather longer than I intended – there’s simply so much to cover with FHIR. But I think we’ve covered the most important bits – enjoy your FHIR journey!

About David Hay
I'm an independent contractor working with a number of Organizations in the health IT space. I'm an HL7 Fellow, Chair Emeritus of HL7 New Zealand and a co-chair of the FHIR Management Group. I have a keen interest in health IT, especially health interoperability with HL7 and the FHIR standard. I'm the author of a FHIR training and design tool - clinFHIR - which is sponsored by InterSystems Ltd.

One Response to FHIR: The important bits

  1. Pingback: Dew Drop – March 9, 2026 (#4620) – Morning Dew by Alvin Ashcraft

Leave a Reply

Discover more from Hay on FHIR

Subscribe now to keep reading and get access to the full archive.

Continue reading