← Methods repository
CONCEPTINTERMEDIATEPYTHON · R2 citations

FHIR and Healthcare Interoperability for RWE

HL7 FHIR (Fast Healthcare Interoperability Resources) is the REST/JSON standard for exchanging clinical data between EHRs, payers, and research systems — not an analysis format but an exchange protocol whose mandatory adoption under US federal interoperability rules (21st Century Cures Act, CMS Interoperability Rule) now makes it a practical on-ramp for prospective RWE collection, patient- mediated data access, payer claims feeds, and EHR-to-research pipelines via bulk FHIR $export.

Data Standardfhirinteroperabilitydata-standardehrclaimsrest-apismart-on-fhirbulk-export
On this page
Methods reference only. Use primary source citations and local policy before applying this in a study protocol, regulatory submission, payer dossier, or clinical decision.
In plain language

FHIR (pronounced "fire") is the modern web standard that hospitals, insurers, and apps use to share patient data with each other — each piece of clinical information (a diagnosis, a prescription, a lab result) is packaged as a small JSON document called a "resource" and delivered over a standard internet connection. US federal rules passed in 2020 and 2021 now require hospitals and insurers to offer FHIR connections, which means researchers can reach prescription histories, lab results, and payer claims in a structured, machine-readable way without starting from scratch at every site.

For an RWE analyst, FHIR is most relevant when designing prospective data-collection pipelines, patient-mediated data-sharing studies, or decentralized trials — for most retrospective studies it is background plumbing that the data-engineering team has already handled before the records reach your desk.

One honest caveat: receiving data in FHIR format does not guarantee the fields you need are populated — completeness still varies by source and must always be checked.

When to use it
Prefer FHIR-based pipelines for prospective, patient-mediated, or real-time data collection; prefer OMOP CDM for retrospective multi-database network studies where standardization and portability are paramount.
Prefer FHIR-based EHR extraction when standardized multi-site access is needed; prefer direct EHR database access (or NLP pipelines) when full clinical depth including unstructured data is required for outcome...
Use FHIR as the transport mechanism for tokenized record exchange and tokenization (PPRL) as the linking key. Neither replaces the other in a multi-source RWE pipeline.
Watch out for
FHIR is an exchange format, not an analysis model; running RWE analyses directly on FHIR resources requires building the analytic translation layer that OMOP CDM already provides — standardized vocabularies,...
FHIR APIs expose only data the source system has structured and consented to share via the standard profile; free-text clinical notes, unstructured imaging reports, and granular visit-level detail beyond US...
FHIR alone does not provide linkage capability — it delivers individual records to authorized systems but matching patients across data sources requires a separate tokenization protocol.

What FHIR is — and is not

HL7 FHIR (Fast Healthcare Interoperability Resources, pronounced "fire") is a standard published by Health Level Seven International (HL7) that defines how clinical data are exchanged over modern web APIs. FHIR structures clinical information as self-contained JSON or XML documents called resources — Patient, Condition, MedicationRequest, Observation, DiagnosticReport, Encounter, and roughly 150 others — each with a fixed schema and each retrievable via standard HTTP calls (GET /fhir/r4/Patient/P001).

FHIR is not an analysis model: it makes no promises about completeness, it allows many fields to be optional, and the same clinical concept can appear in different resource types depending on how a site configured its system. Its job is exchange; what the analyst does with the data after receiving them is a separate problem that requires an ETL step and an analytic data model.

Core FHIR resources for RWE

Four resource types cover most RWE-relevant clinical data:

  • Patient — demographics: birth date, sex, address (useful for area-level SES linkage), identifiers.
  • Condition — diagnoses coded in ICD-10-CM or SNOMED CT, with a `clinicalStatus`, a `verificationStatus` (confirmed vs differential vs refuted), and an onset date. Not every recorded condition is a confirmed clinical finding — an analyst must filter on status fields and validate phenotype definitions against a reference standard before treating Condition resources as case-defining.
  • MedicationRequest — prescription orders including the RxNorm-coded drug concept, the `authoredOn` date, and (in the `dispenseRequest` block) the intended supply duration. This is an order, not proof of dispensing; confirming a fill still requires a MedicationDispense resource or a linked pharmacy claim. Using `authoredOn` as a fill date is a common and often necessary approximation.
  • Observation — laboratory results (LOINC codes), vitals, smoking status, and social history. A single patient's labs return as a bundle of Observation resources, one per LOINC code per encounter date, each with a `valueQuantity` or `valueCodeableConcept` and a `status` field. Filter on `status: final` or `amended` before trusting the value.

A minimal MedicationRequest resource illustrating the key fields an exposure pipeline must parse:

```json { "resourceType": "MedicationRequest", "id": "med-001", "status": "active", "intent": "order", "medicationCodeableConcept": { "coding": [{ "system": "http://www.nlm.nih.gov/research/umls/rxnorm", "code": "860975", "display": "metformin 500 mg oral tablet" }] }, "subject": { "reference": "Patient/P001" }, "authoredOn": "2023-01-05", "dispenseRequest": { "expectedSupplyDuration": { "value": 30, "unit": "days" } } } ```

Regulatory tailwind: why FHIR is now mandatory in the US

Two US regulations made FHIR APIs a legal requirement and created the infrastructure RWE can tap:

  1. 21st Century Cures Act (2016) and ONC Interoperability Rule (2020): EHR developers must expose patient data via a standardized FHIR R4 API. The US Core Implementation Guide specifies which USCDI (United States Core Data for Interoperability) data classes must be supported — medications, conditions, allergies, lab results, vitals, demographics, and care-team members. As of 2023, all ONC-certified EHRs must meet US Core v3.1.1 or later.
  2. CMS Interoperability and Patient Access Rule (2020, effective 2021): Medicare Advantage, Medicaid, CHIP, and most commercial payers must expose member claims data via a FHIR R4 API using the CARIN Blue Button 2.0 implementation guide. This is the first mandatory, standardized payer-side claims API — giving patients and authorized apps machine-readable access to their own payer data including drug fills, inpatient encounters, and outpatient services.

Together these rules mean that by 2024, nearly every US EHR and major payer exposes FHIR APIs, creating a standardized infrastructure that did not exist before 2021. For RWE, this matters because the data-access negotiation no longer has to start from scratch at each site — the API exists, the authentication model (SMART on FHIR OAuth 2.0) is standardized, and the data structure is specified.

Why FHIR matters to an RWE analyst today

FHIR enters RWE practice through four channels:

  1. Patient-mediated data access via SMART on FHIR: Patients can authorize a research app (using the SMART on FHIR OAuth 2.0 flow) to pull their own EHR or payer data and donate it to a study. This enables hybrid prospective-retrospective designs — participants share their existing records at enrollment without requiring a site-level data-use agreement for each health system the patient has visited.
  2. Payer claims via CARIN Blue Button: A patient can authorize a research or care-management app to retrieve their claims history directly from their insurer. The feed is structurally similar to a traditional claims extract — encounters, drug fills, diagnoses as ExplanationOfBenefit resources — but patient-authorized rather than requiring a payer contract.
  3. EHR bulk data ($export): FHIR's asynchronous bulk-export operation returns population-level data as flat NDJSON (newline-delimited JSON) files, one file per resource type, with one resource per line. A researcher granted bulk authorization can download a cohort's MedicationRequest, Condition, and Observation files and parse them directly into analytic tables — closer to a traditional database extract than to patient-by-patient API polling.
  4. Prospective and decentralized trials: FHIR-native electronic data capture (EDC) platforms use MedicationRequest and Observation resources to auto-populate case report forms from EHR data, reducing transcription error and enabling near-real-time safety monitoring in decentralized or virtual study designs.

FHIR vs OMOP — the honest comparison

FHIR and OMOP address different problems and are complementary, not competing. FHIR is an exchange standard: it defines how data travel from system A to system B in a format each system already speaks. OMOP is an analysis model: it defines how data are reorganized into a person-centric, vocabulary- standardized schema optimized for running the same SQL query across dozens of databases.

The typical research pipeline is linear:

`Source EHR/Payer → FHIR API → ETL → OMOP CDM (or flat analytic tables) → Analysis`

This means most RWE analysts working on retrospective cohort studies never interact with FHIR directly. Their data have already been ETL'd into OMOP or a similar analytic CDM by the time they write their first query. FHIR becomes analyst-visible when:

  1. the team is building a prospective or hybrid collection pipeline,
  2. data are being ingested via patient-mediated donation, or
  3. the study uses tokenized linkage where FHIR is the transport for de-identified record exchange.

FHIR-to-OMOP mapping follows a domain-routing logic parallel to native OMOP ETL: MedicationRequest maps to DRUG_EXPOSURE, Condition to CONDITION_OCCURRENCE, Observation to MEASUREMENT or OBSERVATION.

Coding systems carried in FHIR (RxNorm for drugs, ICD-10-CM for conditions, LOINC for labs) align directly with OMOP standard vocabularies, so the vocabulary translation step is straightforward when codes are present. The complication is that FHIR permits multiple coding systems in a single resource and the ETL must apply a priority ordering to select the standard concept when multiple codings compete.

The optionality problem: "FHIR-compatible" does not mean "analyzable"

US Core profiles narrow FHIR's optionality but do not eliminate it. A MedicationRequest is valid FHIR R4 without a `days_supply` value. A Condition resource is valid without an onset date. Two APIs can both be US Core compliant while returning very different field population rates for the same data element.

Receiving a FHIR feed does not guarantee analytic-quality data — the same data-completeness challenges that plague claims and EHR persist inside FHIR, wrapped in JSON. The analysis validity of a FHIR-fed pipeline depends on the field population rates of the specific source system and must be evaluated empirically (field-level missingness reports, value-set conformance checks) before any analytic conclusions are drawn.

Pros, cons, and trade-offs

Pros of FHIR for RWE: Mandatory regulatory adoption creates a standardized, legally guaranteed access pathway that did not exist before 2021 — especially for patient-mediated and payer-side data. REST/JSON is the lingua franca of modern software, lowering the technical barrier versus bespoke database extracts or HL7 v2 feeds.

SMART on FHIR OAuth enables patient consent and data donation at scale without a site-level DUA for every health system. Bulk $export brings population-level extraction feasibility without per-patient API looping. FHIR coding (RxNorm, SNOMED, LOINC) maps cleanly into OMOP vocabularies, reducing the vocabulary harmonization burden in ETL.

Cons and limitations: FHIR is a point-of-exchange format, not an analysis model — data arrive heterogeneous and require explicit ETL into an analytic structure. The optionality of FHIR profiles means "FHIR-available" is not "analysis-ready." Bulk $export requires group-level authorization and a data-use agreement, just like any secondary data source.

Access to bulk operations is not universal — many EHR FHIR implementations support only individual SMART Individual Launch queries, not group or bulk export. For the majority of retrospective RWE analysts, FHIR is entirely invisible plumbing.

When NOT to use — and common misconceptions

  • "We're using FHIR so the data-quality problem is solved." FHIR defines structure, not completeness. A FHIR-compliant feed can have missing supply durations, absent onset dates, and inconsistent lab units. Field-population checks are mandatory before analysis.
  • "I can run my OMOP analysis directly on FHIR resources." FHIR and OMOP have different schemas, coding conventions, and temporal structures. An explicit ETL is required; do not skip it.
  • "Patient-authorized FHIR data are complete for drug exposure." A patient's payer FHIR feed covers only that insurer's claims — out-of-pocket fills, OTC drugs, and care under another plan are invisible. The completeness ceiling is the enrollment scope of the authorizing payer.
  • "All FHIR APIs support bulk $export." Bulk export is a capability layered on FHIR, not guaranteed by FHIR R4 conformance alone. Many EHR FHIR APIs support only SMART Individual Launch (one patient at a time); bulk access requires separate negotiation.
  • Using repeated individual patient-level API calls as a population extraction strategy: polling thousands of patient records one at a time creates rate-limit pressure, audit-log burdens, and re-identification risk. Prefer bulk $export with appropriate authorization for population-scale work.

Interpreting the output

In the worked example, parsing three FHIR MedicationRequest resources for patient P001 yields three rows in an analytic exposure table — one row per resource — each carrying the RxNorm code (860975), the authoredOn prescription date used as fill_date, and the days_supply extracted from `dispenseRequest.expectedSupplyDuration.value`. Total days_supply across three rows = 90.

(1) Formal interpretation. Each analytic row is a 1:1 extract of one FHIR MedicationRequest resource: no aggregation, no period-collapsing, and no assumption about whether the drug was actually dispensed or taken. The `authoredOn` field is the prescriber order date — the closest proxy to a pharmacy fill date available in the MedicationRequest resource but not equivalent to a dispensing date.

The days_supply sum of 90 represents cumulative intended treatment duration across three sequential orders; gaps between fills (visible in the date sequence) are not yet evaluated. Adherence calculation requires a downstream analytic step (PDC or MPR) that applies a treatment window and denominator.

(2) Practical interpretation. When a data engineer delivers three exposure rows with the same RxNorm code on dates roughly 30 days apart, the analyst should verify before proceeding:

  1. that `days_supply` was actually populated in all rows — it is optional in FHIR and the pipeline must document what happens when it is null;
  2. that `intent` equals "order" not "proposal" or "plan";
  3. whether a corresponding MedicationDispense or pharmacy claim confirming the fill is available.

These verifications are the FHIR-pipeline analog of source-data audit and should be specified in the statistical analysis plan before the pipeline is built, not discovered during analysis.

Decision diagram

flowchart LR
  subgraph SRC["Source Systems (US FHIR Mandates)"]
    EHR["EHR\n(ONC-certified FHIR R4)"]
    PAY["Payer\n(CMS CARIN mandate)"]
  end
  subgraph FHIR["FHIR Exchange APIs"]
    SMART["SMART on FHIR\nindividual patient pull\npatient-authorized"]
    BULK["Bulk dollar-export\npopulation NDJSON\nresearch authorization"]
    CARIN["CARIN Blue Button 2.0\nclaims as\nExplanationOfBenefit"]
  end
  subgraph ETL["ETL / Transform"]
    PARSE["Parse resources\nExtract RxNorm/ICD/LOINC\nNull-handle optional fields"]
  end
  subgraph ANA["Analytic Layer"]
    OMOP["OMOP CDM\nmulti-site portable SQL"]
    FLAT["Flat analytic tables\nsingle-site study"]
  end
  EHR --> SMART
  EHR --> BULK
  PAY --> CARIN
  SMART --> PARSE
  BULK --> PARSE
  CARIN --> PARSE
  PARSE --> OMOP
  PARSE --> FLAT
FHIR as transport layer between source systems and the analytic destination. FHIR APIs (SMART on FHIR for individual access, bulk $export for populations, CARIN Blue Button for payer claims) feed an ETL that produces OMOP CDM or flat analytic tables. FHIR is the exchange format; OMOP is the analysis model.

Worked example

Scenario

A data engineer is building an exposure-extraction pipeline for a new-user cohort study on metformin initiation. The study site exposes a FHIR R4 bulk $export endpoint. For patient P001, the engineer retrieves three MedicationRequest resources from the NDJSON export file, extracts four fields from each resource, and assembles a three-row analytic exposure table. The goal is to confirm that the 1-to-1 resource-to-row mapping is exact and to sum the intended days of supply across all three rows.

Dataset

Four fields extracted from three FHIR MedicationRequest resources for patient P001. The rxnorm_code comes from medicationCodeableConcept.coding where system equals the RxNorm URI; authoredOn is the prescription order date; days_supply comes from dispenseRequest.expectedSupplyDuration.value. RxNorm code 860975 = metformin 500 mg oral tablet (illustrative).

resource_idauthoredOnrxnorm_codedays_supply
med-0012023-01-0586097530
med-0022023-02-0686097530
med-0032023-03-0986097530
FIG. 1 — DESIGN TIMELINE
Three FHIR MedicationRequest resources for patient P001 (metformin 500 mg, Q1 2023)
Three FHIR MedicationRequest resources for patient P001 (metformin 500 mg, Q1 2023)

Steps

1Parse each NDJSON line as a MedicationRequest JSON object; confirm resourceType equals "MedicationRequest" and status equals "active" before extracting any fields — resources with status "cancelled" or "entered-in-error" are excluded.
2Extract the RxNorm code from medicationCodeableConcept.coding by selecting the entry whose system equals "http://www.nlm.nih.gov/research/umls/rxnorm"; all three resources carry code 860975 (metformin 500 mg oral tablet).
3Extract the prescription date from the authoredOn field; the three dates are 2023-01-05, 2023-02-06, and 2023-03-09 — these are order dates, not confirmed dispensing dates.
4Extract the intended supply duration from dispenseRequest.expectedSupplyDuration.value; all three resources carry a value of 30 days. Note that this field is optional in FHIR — if absent, days_supply is null and must be imputed or the row flagged for review.
5Write one analytic exposure row per MedicationRequest resource; 3 resources produce 3 rows because the mapping is exactly 1-to-1 at extraction — no aggregation or deduplication is applied.
6Sum total intended days of exposure: 30 + 30 + 30 = 90 days. This is the raw maximum intended exposure across three sequential orders, not an adherence measure.

Result

3 MedicationRequest resources produce exactly 3 analytic exposure rows (1-to-1 mapping confirmed). RxNorm code 860975 appears in all three rows. Total days_supply = 30 + 30 + 30 = 90 days of intended exposure for patient P001. Gaps between order dates (Feb 4 to Feb 5; Mar 8) are visible in the date sequence but are not yet evaluated — adherence calculation is a downstream analytic step.

Trade-offs

Pros of this
FHIR APIs are available directly from source systems (EHRs, payers) without a separate ETL step, enabling real-time or near-real-time data access for prospective designs and patient-mediated data collection across sites that would otherwise require bespoke per-site database extracts.
Pros of this
FHIR APIs provide a standardized, federally mandated access pathway to EHR data that reduces reliance on bespoke per-site database extracts; SMART on FHIR enables patient-authorized access without requiring traditional data-use agreements for each health system.
Pros of this
FHIR provides a standardized transport for individual patient records; when combined with tokenization, FHIR feeds enable privacy-preserving record linkage across payers and EHRs without exposing direct identifiers — the transport standard and the privacy technique are complementary layers, not alternatives.

Runnable example

Parse FHIR MedicationRequest resources from a simulated NDJSON bulk-export string into a pandas DataFrame. Extracts RxNorm code, prescription date (authoredOn), and intended days_supply from each resource. Handles the optional dispenseRequest block gracefully.

import json
import pandas as pd
from io import StringIO

# ── Simulated NDJSON bulk export: 3 MedicationRequest resources (one per line) ──
ndjson_data = (
    '{"resourceType":"MedicationRequest","id":"med-001","status":"active","intent":"order",'
    '"medicationCodeableConcept":{"coding":[{"system":"http://www.nlm.nih.gov/research/umls/rxnorm",'
    '"code":"860975","display":"metformin 500 mg oral tablet"}]},'
    '"subject":{"reference":"Patient/P001"},"authoredOn":"2023-01-05",'
    '"dispenseRequest":{"expectedSupplyDuration":{"value":30,"unit":"days"}}}\n'
    '{"resourceType":"MedicationRequest","id":"med-002","status":"active","intent":"order",'
    '"medicationCodeableConcept":{"coding":[{"system":"http://www.nlm.nih.gov/research/umls/rxnorm",'
    '"code":"860975","display":"metformin 500 mg oral tablet"}]},'
    '"subject":{"reference":"Patient/P001"},"authoredOn":"2023-02-06",'
    '"dispenseRequest":{"expectedSupplyDuration":{"value":30,"unit":"days"}}}\n'
    '{"resourceType":"MedicationRequest","id":"med-003","status":"active","intent":"order",'
    '"medicationCodeableConcept":{"coding":[{"system":"http://www.nlm.nih.gov/research/umls/rxnorm",'
    '"code":"860975","display":"metformin 500 mg oral tablet"}]},'
    '"subject":{"reference":"Patient/P001"},"authoredOn":"2023-03-09",'
    '"dispenseRequest":{"expectedSupplyDuration":{"value":30,"unit":"days"}}}\n'
)

RXNORM_SYSTEM = "http://www.nlm.nih.gov/research/umls/rxnorm"

def extract_rxnorm_code(resource):
    codings = resource.get("medicationCodeableConcept", {}).get("coding", [])
    for c in codings:
        if c.get("system") == RXNORM_SYSTEM:
            return c.get("code")
    return None

def extract_days_supply(resource):
    return (resource
            .get("dispenseRequest", {})
            .get("expectedSupplyDuration", {})
            .get("value"))

rows = []
for line in StringIO(ndjson_data):
    line = line.strip()
    if not line:
        continue
    resource = json.loads(line)
    if resource.get("resourceType") != "MedicationRequest":
        continue
    if resource.get("status") not in ("active", "completed"):
        continue
    person_id = resource["subject"]["reference"].split("/")[-1]
    rows.append({
        "person_id":   person_id,
        "resource_id": resource["id"],
        "fill_date":   resource.get("authoredOn"),
        "rxnorm_code": extract_rxnorm_code(resource),
        "days_supply": extract_days_supply(resource),
    })

exposure_df = pd.DataFrame(rows)
print(exposure_df.to_string(index=False))
# person_id resource_id  fill_date rxnorm_code  days_supply
#      P001    med-001  2023-01-05      860975         30.0
#      P001    med-002  2023-02-06      860975         30.0
#      P001    med-003  2023-03-09      860975         30.0

total_days = int(exposure_df["days_supply"].sum())
print(f"Resource count: {len(exposure_df)}")   # 3
print(f"Total days_supply: {total_days}")       # 90
assert len(exposure_df) == 3, "Expected 3 rows from 3 MedicationRequest resources"
assert total_days == 90, "Expected 30 + 30 + 30 = 90"

Citations

FOUNDATIONAL / METHODS
  1. [1]Mandel JC, Kreda DA, Mandl KD, Kohane IS, Ramoni RB. SMART on FHIR: a standards-based, interoperable apps platform for electronic health records. Journal of the American Medical Informatics Association. 2016;23(5):899-908.
  2. [2]Vorisek CN, Lehne M, Klopfenstein SAI, et al. Fast Healthcare Interoperability Resources (FHIR) for interoperability in health research: systematic review. JMIR Medical Informatics. 2022;10(7):e35724.