FHIR References

Overview

FHIR resources don’t embed data from other resources — they reference them. An Observation doesn’t copy the patient’s name and date of birth into itself; it holds a reference to a Patient resource. That reference is a URL, a logical identifier, or a contained copy of the target resource.

This sounds simple. In practice, references are where a significant fraction of FHIR integration problems originate: broken resolution, incorrect reference types, contained resources used where they shouldn’t be, and identity ambiguity when the same patient exists on multiple servers.

Understanding reference mechanics is a prerequisite for correct navigation, search, and data integrity validation.


The Reference Data Type

The Reference data type has four fields:

Reference.reference   — the URL string (relative or absolute)
Reference.type        — the expected resource type ("Patient", "Practitioner", etc.)
Reference.identifier  — a logical identifier (for logical references)
Reference.display     — human-readable label for rendering

You rarely populate all four at once. The combination you use determines what kind of reference you have.


The Four Reference Patterns

1. Relative Reference

A path relative to the server base URL where the resource lives.

{
  "reference": "Patient/123"
}

This is the most common form for resources on the same server. The client resolves it by prepending the server base URL: https://server.example.org/fhir/Patient/123.

Relative references are clean, portable within a server, and unambiguous when the server base is known. Use them for all same-server references.

The type field is technically redundant when the reference string includes the resource type (Patient/123), but including it makes the reference self-describing and helps parsers that need the type before resolving.

2. Absolute Reference

A fully qualified URL including the server base.

{
  "reference": "https://ehr.hospital.org/fhir/Patient/456",
  "type": "Patient"
}

Use absolute references when the target resource lives on a different server. The receiving system can resolve it independently, without needing to know any base URL.

The trap: absolute references that include an environment-specific base URL (dev, staging, prod) will break when resources move or when the same data is loaded into a different environment. Hardcoding https://dev.ehr.example.org/fhir/Patient/456 in a resource that gets promoted to production creates broken references.

3. Logical Reference (Identifier-Based)

A reference by business identifier when the target resource is not at a known URL.

{
  "type": "Patient",
  "identifier": {
    "system": "http://hl7.org/fhir/sid/us-ssn",
    "value": "555-12-3456"
  },
  "display": "Jane Smith"
}

No reference string. The receiving system must resolve this by querying for a Patient with that identifier.

Payers and labs use this pattern constantly. When a lab result arrives from an external lab system, the lab knows the patient by MRN or insurance ID but doesn’t have access to the payer’s Patient resource URL. A logical reference lets the lab identify the patient unambiguously without needing FHIR connectivity to the payer’s server.

The identifier.assigner field (a Reference to an Organization) names who issued the identifier, which matters when the same identifier value appears in multiple systems.

Logical references require the receiving system to do the resolution work. If it can’t find a matching resource, the reference is effectively broken — but it’s broken in a recoverable way, unlike a relative reference to a deleted resource.

4. Contained Reference

The target resource is embedded directly inside the referencing resource, under contained.

{
  "resourceType": "Observation",
  "id": "obs-789",
  "contained": [
    {
      "resourceType": "Patient",
      "id": "patient-contained-1",
      "name": [{ "family": "Anonymous", "given": ["Patient"] }]
    }
  ],
  "subject": {
    "reference": "#patient-contained-1"
  },
  "status": "final",
  "code": {
    "coding": [{ "system": "http://loinc.org", "code": "1975-2" }]
  }
}

The # prefix in the reference string means “look inside contained for a resource with this id”.


When to Use Each Type

Relative: Default for same-server references. Most FHIR servers create resources this way.

Absolute: Cross-server references, or when resources will be exchanged outside any specific server context (documents, messages). Be careful about environment-specific URLs baking in to long-lived data.

Logical: When the referenced resource exists in a system you don’t have direct FHIR access to, or when the exchange convention is identifier-based rather than URL-based. Common in payer workflows and lab reporting.

Contained: Narrow use case — tightly coupled data that has no useful independent identity and will not be referenced from anywhere else. The canonical example is an Observation that needs a performer whose identity is only meaningful in the context of that specific Observation (a practitioner identified by the sending system but not registered in any master directory). Another is a Medication referenced from a MedicationRequest when the medication has no independent FHIR record.


Contained Resource Rules

The FHIR spec places hard constraints on contained resources that are routinely violated by implementations:

  1. Contained resources cannot contain other contained resources. No nesting.
  2. Contained resources cannot be referenced from outside the container. If any other resource might reference this data, it should not be contained.
  3. Contained resources cannot reference resources outside their container — with one exception: they may reference the container itself.
  4. Contained resources must have an id so the containing resource can reference them with #id.
  5. A contained resource has no independent existence. If you delete or update the container, the contained resource goes with it.

The most common violation: using contained for data that other resources reference independently. An Organization that is the performer on multiple Observations should be a standalone resource, not contained in each Observation.


Reference Resolution

When a client encounters a reference, resolution depends on the reference type:

Relative reference: Prepend the server base URL. Patient/123 on a server with base https://server.example.org/fhir resolves to https://server.example.org/fhir/Patient/123. The “server base URL” is whatever URL you originally sent the search or read request to — not necessarily what’s in Bundle.entry.fullUrl.

Absolute reference: Resolve by HTTP GET to the URL. The client needs appropriate credentials for the target server — which may be a different auth domain.

Logical reference: Resolve by searching the target server for a resource with the given identifier. Clients that don’t implement this resolution step will silently drop the reference.

Contained reference: Look up the id in Resource.contained[]. No HTTP request.

The server base URL problem: when resources are exchanged outside their origin server (exported as a file, sent as a message, accessed through a gateway), relative references become ambiguous. The receiver doesn’t know what base URL to prepend. Documents (composition bundles) address this by including fullUrl on every entry and resolving references against that.


Referential Integrity

FHIR does not mandate referential integrity. A server may accept a resource that references a non-existent target. It may also delete a resource that other resources reference.

This is a deliberate design choice for flexibility, not an oversight. Healthcare systems receive data from external sources that reference entities the receiving system doesn’t have. Rejecting those resources because the Patient isn’t locally present would prevent import of valid clinical data.

What this means for receiving systems: you cannot assume a reference is resolvable. Build your processing logic to handle unresolvable references gracefully — log them, flag the resource for review, or fall back to display or identifier for human-readable rendering.

Some servers offer optional referential integrity enforcement via their CapabilityStatement (rest.resource.referencePolicy). Values include literal (server checks all references), logical (server resolves logical references), and enforced (server rejects resources with broken references). Check the CapabilityStatement if you need to know what a specific server enforces.


References aren’t just navigation targets — they’re search dimensions. FHIR search lets you chain through references using dot notation:

GET [base]/Observation?patient.name=Smith
GET [base]/Observation?patient.birthdate=1978-04-12
GET [base]/DiagnosticReport?result.code=http://loinc.org|1975-2

Each dot traverses one reference. The first segment is the search parameter name on the primary resource type, the part after the dot is the search parameter on the referenced resource type.

When a reference can point to multiple resource types, you qualify it:

GET [base]/Observation?performer:Practitioner.name=Singh

Reverse chaining (_has) works in the opposite direction — give me resources that are referenced by resources meeting some criterion:

GET [base]/Patient?_has:Observation:patient:code=http://loinc.org|1975-2

_include and References

_include follows references to add the referenced resources to the search result Bundle. This is the primary way to retrieve a set of Observations along with their Patients in a single request, rather than N+1 calls:

GET [base]/Observation?category=laboratory&_include=Observation:patient

The Bundle will contain Observation matches (mode: match) and Patient resources they reference (mode: include). See the FHIR Search article for full _include and _revinclude documentation.


Anti-Patterns

Hardcoded absolute URLs in data that moves. Resources stored in one environment with environment-specific absolute URLs break when promoted or migrated. Prefer relative references for same-server data or design your URLs to be environment-agnostic.

Omitting type on polymorphic references. Some reference elements allow multiple resource types (e.g., Observation.subject can be Patient, Group, Device, or Location). If you omit type and only provide a bare ID like subject: { "reference": "456" }, the receiver cannot know what type to fetch. Include type on all references where the resource type is not implied by the reference string.

Contained resources for independently lifecycle’d data. The moment you find yourself thinking “I’ll just contain this Organization in the Observation because it’s easier,” ask: will any other resource ever reference this Organization? Will it exist before or after this Observation? If yes to either, it should be a standalone resource.

Ignoring display when resolution fails. The display field is not normative — it can be stale — but when a reference is unresolvable, it’s the only human-readable fallback. Rendering systems should use it rather than showing a raw ID.

Section: fhir Content Type: reference Audience: technical
FHIR Versions:
R4 R5
Published: 15/08/2022 Modified: 09/12/2025 12 min read
Keywords: FHIR references contained resources logical reference reference resolution referential integrity chained parameters
Sources: