1. Exchanging Data vs. Exchanging Meaning
FHIR solved the data exchange problem. A hospital can now send a FHIRObservation resource to a research platform, a FHIR MedicationStatement to a safety database, or a FHIR Condition to a registry. The structure is standardized. The JSON is valid. The API works.
But open the payload and look at the CodeableConcept - the field that carries the clinical meaning. You’ll find LOINC codes from one system, proprietary local codes from another, and sometimes just a text field with “Blood Sugar” and no structured code at all. The data exchanged successfully. The meaning got lost in translation.
This is the semantic interoperability gap. FHIR standardizes structure; OMOP standardizes vocabulary. To go from exchanged FHIR data to analyzable OMOP records, you need to resolve every CodeableConcept to a standard OMOP concept ID.
OMOPHub handles that resolution step. It’s a vocabulary API - you give it a code or a search term, it gives you back the standard OMOP concept. It doesn’t parse FHIR resources, transform data structures, or load OMOP tables. But it answers the question every FHIR-to-OMOP pipeline needs answered: “What OMOP concept ID does this CodeableConcept map to?”
This article is a cookbook: specific FHIR resource types, specific OMOPHub API calls, specific OMOP CDM outputs.
Note: This article complements the EHR Integration article, which covers the architectural patterns. This one goes resource-by-resource with concrete examples.
2. The Core Pattern: Resolving a FHIR CodeableConcept
Every FHIR clinical resource contains CodeableConcept fields. The resolution pattern is the same regardless of resource type:
Step 1: Check the coding array for known vocabularies.
The coding array may contain codes from LOINC, SNOMED, RxNorm, ICD-10-CM, NDC, or local systems. You need an explicit mapping from FHIR system URIs to OMOP vocabulary IDs:
Python
text field.
Search OMOPHub with the human-readable text using search.basic() or search.semantic(), filtered to the expected vocabulary and domain.
Step 3: Get the standard concept.
If the found concept is non-standard (e.g., an ICD-10-CM source concept), call mappings.get() to find the “Maps to” standard equivalent (usually SNOMED for conditions, RxNorm for drugs, LOINC for measurements).
3. Use Case A: FHIR Observation → OMOP Measurement (Labs & Vitals)
The most common FHIR-to-OMOP mapping. Lab results and vital signs arrive asObservation resources and need to become OMOP measurement records.
The Scenario: A blood glucose Observation arrives with LOINC code 2339-0. Resolve it to an OMOP concept and build a measurement record.
Python
search.basic() call with vocabulary_ids=["LOINC"] finds it directly. No “transformation” needed - just vocabulary resolution. The fallback to text search handles the cases where EHRs send local codes instead of LOINC.
4. Use Case B: FHIR MedicationStatement → OMOP Drug Exposure (The Ingredient Bridge)
Medication reconciliation requires rolling specific product codes up to their active ingredient for class-level analysis. The Scenario: A FHIRMedicationStatement arrives with NDC code “0071-0155-01” (Lipitor 10mg). For a drug safety study, you need the active ingredient (Atorvastatin), not the branded product.
Python
search.basic() to find the NDC concept, mappings.get() to cross to RxNorm, and concepts.relationships() to find the ingredient. “Has ingredient” is a relationship, not a hierarchical ancestor - an important distinction. The OMOP vocabulary encodes drug composition as relationships between concepts, not as parent-child hierarchy levels. Your pharmacy sees “Lipitor 10mg.” Your research database needs “Atorvastatin.” OMOPHub bridges the gap.
5. Handling FHIR Extensions and Profiles
Real-world FHIR uses extensions - extra data fields not in the base spec. The US Core Profile, for example, adds detailed race and ethnicity extensions toPatient resources.
How this maps to OMOP:
- Smoking status extension →
OBSERVATIONtable with a standard SNOMED concept - Detailed race/ethnicity →
person.race_concept_idandperson.ethnicity_concept_id - Patient-reported outcomes →
OBSERVATIONorMEASUREMENTdepending on type
CodeableConcept values, the same resolution pattern applies - extract the code, search OMOPHub, get the standard concept ID. When extensions contain text or categorical values, you may need to search OMOPHub by the display text to find the appropriate OMOP concept.
The parsing of FHIR extensions (identifying them by URL, extracting their values) is application code - OMOPHub handles the vocabulary resolution for whatever coded or text values you extract.
6. Conclusion: From Structure to Meaning
FHIR gives you structured data exchange. OMOP gives you standardized vocabularies. OMOPHub bridges the vocabulary gap between them - oneCodeableConcept at a time.
The pattern is the same for every FHIR resource type: extract the coding array → look up each code in OMOPHub → get the standard OMOP concept → fall back to text search if needed. Whether it’s an Observation becoming a measurement, a MedicationStatement becoming a drug_exposure, or a Condition becoming a condition_occurrence, the vocabulary resolution step is OMOPHub’s job.
Build the FHIR-system-to-OMOP-vocabulary lookup table. Implement the code-first, text-fallback resolution pattern. Let OMOPHub handle the vocabulary mapping while your application handles the FHIR parsing and OMOP loading. That separation of concerns is what makes the pipeline maintainable across EHR installations that all speak slightly different FHIR dialects.