📝
Community-Driven Guide
Help improve this guide with your profiling patterns and experience

FHIR Data Modeling & Profiling Design Guide

Master the art of designing FHIR profiles using the proven "Learn, Build, Use" framework

Learn → Understand FHIR base specifications
Build → Design custom profiles
Use → Implement and validate

Introduction to FHIR Profiling

FHIR profiling is the process of constraining and extending FHIR base resources to meet specific implementation requirements. This guide uses the "Learn, Build, Use" framework to help you master FHIR data modeling and profiling design.

What is FHIR Profiling?

Profiling allows you to adapt FHIR's generic resources for your specific use case by:

  • Constraining: Making optional elements required, limiting value sets, or restricting cardinality
  • Extending: Adding new elements that don't exist in the base resource
  • Documenting: Providing clear usage guidance for implementers
  • Validating: Ensuring data conforms to your business requirements

The Learn, Build, Use Framework

📚

LEARN

Understand FHIR specifications, implementation guides, and existing patterns

🔨

BUILD

Design and create profiles that align with your business requirements

🚀

USE

Implement, validate, and iterate on your profiles in production

📚

Phase 1: LEARN

Step 1: Understanding FHIR Base Specifications

Start with the Fundamentals

  • Resource Structure: Every FHIR resource has:
    • • Metadata (id, meta, implicitRules, language)
    • • Narrative (text - human-readable summary)
    • • Extensions (for additional data not in base spec)
    • • Resource-specific elements (Patient.name, Observation.value, etc.)
  • Data Types: Understand primitive types (string, integer, boolean) and complex types (CodeableConcept, Reference, Period)
  • Cardinality: Elements can be 0..1 (optional), 1..1 (required), 0..* (optional repeating), or 1..* (required repeating)

Key Resources to Study

// Patient - Demographics and administrative information
// Observation - Measurements and simple assertions
// Condition - Problems, diagnoses
// Procedure - Events and interventions
// MedicationRequest - Prescriptions and orders
// Encounter - Interactions between patient and healthcare provider
// Organization - Healthcare providers, payers
// Practitioner - Healthcare professionals

Step 2: Aligning Business Goals with FHIR

Business Analysis Framework

1. Define Your Use Case

What problem are you solving? Who are the stakeholders?

Example: "We need to exchange lab results between hospital labs and primary care physicians"

2. Identify Required Data Elements

What information must be captured? What's optional vs. required?

Example: Test name, result value, reference ranges, performing lab, collection date

3. Map to FHIR Resources

Which FHIR resources best represent your data?

Example: Observation (for results), DiagnosticReport (for report), Specimen (for sample)

4. Determine Constraints

What rules and validations does your business require?

Example: Result value must be present, status must be 'final' or 'amended'

Step 3: Research Implementation Guides

Don't reinvent the wheel! Research existing implementation guides (IGs) that address similar use cases.

US Core Implementation Guide

Foundational profiles for US healthcare. Start here for most US implementations.

View US Core IG →

International Patient Summary

Global standard for patient summaries. Good for cross-border use cases.

View IPS IG →

CARIN Blue Button

Payer data exchange profiles. Essential for claims and coverage data.

View CARIN BB IG →

Da Vinci Project

Value-based care IGs including prior auth, care gaps, and risk adjustment.

View Da Vinci IGs →

Research Checklist:

  • ✓ Does an existing IG already solve my use case?
  • ✓ What profiles from existing IGs can I reuse?
  • ✓ What terminology (value sets, code systems) is already standardized?
  • ✓ What extensions are commonly used in my domain?
  • ✓ What are the conformance expectations (SHALL, SHOULD, MAY)?
🔨

Phase 2: BUILD

Step 4: Design Your Profile

Now that you've learned FHIR and researched existing patterns, it's time to design your profile.

Profile Design Principles

  1. 1. Start Simple, Then Constrain

    Begin with the base resource. Add constraints incrementally based on real requirements.

  2. 2. Reuse Before Creating

    Use existing profiles (like US Core) as your base. Only create new profiles when necessary.

  3. 3. Make Required What Must Be Required

    Only set min=1 (required) if the data is truly essential. Over-constraining makes adoption difficult.

  4. 4. Use Standard Terminologies

    Leverage LOINC, SNOMED CT, RxNorm, and other standard code systems wherever possible.

  5. 5. Document Everything

    Clear definitions and usage notes are crucial for implementer success.

Example: Lab Result Profile

Let's design a profile for lab results that builds on US Core Observation Lab:

{
  "resourceType": "StructureDefinition",
  "url": "http://example.org/fhir/StructureDefinition/lab-result",
  "name": "LabResult",
  "title": "Laboratory Result Profile",
  "status": "draft",
  "description": "Profile for lab test results in our system",
  "fhirVersion": "4.0.1",
  "kind": "resource",
  "abstract": false,
  "type": "Observation",
  "baseDefinition": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab",
  "derivation": "constraint",

  "differential": {
    "element": [
      {
        "id": "Observation.status",
        "path": "Observation.status",
        "short": "Result status",
        "definition": "Status of the lab result",
        "mustSupport": true,
        "binding": {
          "strength": "required",
          "valueSet": "http://example.org/fhir/ValueSet/lab-result-status"
        }
      },
      {
        "id": "Observation.category",
        "path": "Observation.category",
        "min": 1,
        "mustSupport": true,
        "patternCodeableConcept": {
          "coding": [{
            "system": "http://terminology.hl7.org/CodeSystem/observation-category",
            "code": "laboratory"
          }]
        }
      },
      {
        "id": "Observation.code",
        "path": "Observation.code",
        "short": "Lab test code (LOINC preferred)",
        "mustSupport": true,
        "binding": {
          "strength": "extensible",
          "valueSet": "http://example.org/fhir/ValueSet/lab-test-codes"
        }
      },
      {
        "id": "Observation.value[x]",
        "path": "Observation.value[x]",
        "short": "Result value",
        "min": 1,
        "mustSupport": true
      },
      {
        "id": "Observation.interpretation",
        "path": "Observation.interpretation",
        "short": "High, low, normal, etc.",
        "mustSupport": true
      },
      {
        "id": "Observation.referenceRange",
        "path": "Observation.referenceRange",
        "short": "Reference range for result interpretation",
        "min": 0,
        "max": "*",
        "mustSupport": true
      },
      {
        "id": "Observation.performer",
        "path": "Observation.performer",
        "short": "Laboratory that performed the test",
        "min": 1,
        "type": [{
          "code": "Reference",
          "targetProfile": [
            "http://hl7.org/fhir/us/core/StructureDefinition/us-core-organization"
          ]
        }],
        "mustSupport": true
      }
    ]
  }
}

Key Profile Elements Explained:

  • baseDefinition: We build on US Core Observation Lab (don't start from scratch!)
  • derivation: constraint: We're constraining, not creating a new resource type
  • min: 1: Makes optional elements required for our use case
  • mustSupport: true: Implementers must be able to process this element
  • binding.strength: How strictly codes must match the value set (required, extensible, preferred, example)

Step 5: Define Extensions

When FHIR base resources don't have an element you need, create an extension:

{
  "resourceType": "StructureDefinition",
  "url": "http://example.org/fhir/StructureDefinition/lab-urgency",
  "name": "LabUrgency",
  "title": "Lab Test Urgency Extension",
  "status": "draft",
  "kind": "complex-type",
  "abstract": false,
  "context": [{
    "type": "element",
    "expression": "Observation"
  }],
  "type": "Extension",
  "baseDefinition": "http://hl7.org/fhir/StructureDefinition/Extension",
  "derivation": "constraint",

  "differential": {
    "element": [{
      "id": "Extension",
      "path": "Extension",
      "short": "Lab test urgency level",
      "definition": "Indicates the clinical urgency of the lab test (routine, urgent, stat)",
      "max": "1"
    }, {
      "id": "Extension.url",
      "path": "Extension.url",
      "fixedUri": "http://example.org/fhir/StructureDefinition/lab-urgency"
    }, {
      "id": "Extension.value[x]",
      "path": "Extension.value[x]",
      "type": [{
        "code": "code"
      }],
      "binding": {
        "strength": "required",
        "valueSet": "http://example.org/fhir/ValueSet/lab-urgency-codes"
      }
    }]
  }
}

// Usage in an Observation:
{
  "resourceType": "Observation",
  "id": "lab-example",
  "extension": [{
    "url": "http://example.org/fhir/StructureDefinition/lab-urgency",
    "valueCode": "stat"
  }],
  // ... rest of observation
}

Step 6: Create Value Sets

Value sets define the allowed codes for coded elements:

{
  "resourceType": "ValueSet",
  "url": "http://example.org/fhir/ValueSet/lab-result-status",
  "name": "LabResultStatus",
  "title": "Lab Result Status Codes",
  "status": "draft",
  "description": "Allowed status values for lab results",
  "compose": {
    "include": [{
      "system": "http://hl7.org/fhir/observation-status",
      "concept": [
        {
          "code": "final",
          "display": "Final"
        },
        {
          "code": "amended",
          "display": "Amended"
        },
        {
          "code": "corrected",
          "display": "Corrected"
        },
        {
          "code": "preliminary",
          "display": "Preliminary"
        }
      ]
    }]
  }
}
🚀

Phase 3: USE

Step 7: Validation and Testing

Validation Strategy

  1. 1. Use the FHIR Validator
    java -jar validator_cli.jar \
      -version 4.0.1 \
      -ig your-implementation-guide.json \
      -profile http://example.org/fhir/StructureDefinition/lab-result \
      patient-example.json
  2. 2. Create Test Fixtures

    Build a library of valid and invalid examples to test your profiles

  3. 3. Automate Validation

    Integrate validation into your CI/CD pipeline

  4. 4. Test Edge Cases

    Minimum data, maximum data, optional elements, extensions

Example: Valid Lab Result

{
  "resourceType": "Observation",
  "id": "lab-glucose-example",
  "meta": {
    "profile": [
      "http://example.org/fhir/StructureDefinition/lab-result"
    ]
  },
  "extension": [{
    "url": "http://example.org/fhir/StructureDefinition/lab-urgency",
    "valueCode": "routine"
  }],
  "status": "final",
  "category": [{
    "coding": [{
      "system": "http://terminology.hl7.org/CodeSystem/observation-category",
      "code": "laboratory",
      "display": "Laboratory"
    }]
  }],
  "code": {
    "coding": [{
      "system": "http://loinc.org",
      "code": "2339-0",
      "display": "Glucose [Mass/volume] in Blood"
    }],
    "text": "Blood Glucose"
  },
  "subject": {
    "reference": "Patient/example",
    "display": "Jane Doe"
  },
  "effectiveDateTime": "2025-01-15T08:30:00Z",
  "issued": "2025-01-15T09:45:00Z",
  "performer": [{
    "reference": "Organization/lab-acme",
    "display": "ACME Laboratory Services"
  }],
  "valueQuantity": {
    "value": 95,
    "unit": "mg/dL",
    "system": "http://unitsofmeasure.org",
    "code": "mg/dL"
  },
  "interpretation": [{
    "coding": [{
      "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
      "code": "N",
      "display": "Normal"
    }]
  }],
  "referenceRange": [{
    "low": {
      "value": 70,
      "unit": "mg/dL",
      "system": "http://unitsofmeasure.org",
      "code": "mg/dL"
    },
    "high": {
      "value": 100,
      "unit": "mg/dL",
      "system": "http://unitsofmeasure.org",
      "code": "mg/dL"
    },
    "type": {
      "coding": [{
        "system": "http://terminology.hl7.org/CodeSystem/referencerange-meaning",
        "code": "normal",
        "display": "Normal Range"
      }]
    }
  }]
}

Step 8: Implementation and Iteration

Implementation Checklist:

  • □ Publish Your Implementation Guide

    Use the FHIR IG Publisher to create browsable documentation

  • □ Provide Example Resources

    Include valid examples for each profile

  • □ Document Conformance Expectations

    Clear SHALL/SHOULD/MAY requirements

  • □ Set Up a Test Server

    Allow implementers to test against your profiles

  • □ Gather Feedback

    Iterate based on real-world implementation experience

  • □ Version Your Profiles

    Use semantic versioning to manage changes

Best Practices Summary

✓ DO

  • ✅ Start with existing IGs and profiles
  • ✅ Use standard terminologies (LOINC, SNOMED, RxNorm)
  • ✅ Keep profiles as simple as possible
  • ✅ Provide clear documentation and examples
  • ✅ Test thoroughly with real data
  • ✅ Version your profiles appropriately
  • ✅ Engage with the FHIR community
  • ✅ Use mustSupport judiciously
  • ✅ Consider backward compatibility
  • ✅ Document your design decisions

✗ DON'T

  • ❌ Over-constrain optional elements
  • ❌ Create custom code systems unnecessarily
  • ❌ Profile without understanding the base resource
  • ❌ Ignore existing implementation patterns
  • ❌ Skip validation and testing
  • ❌ Make breaking changes in minor versions
  • ❌ Create extensions for data that fits existing elements
  • ❌ Use too many nested extensions
  • ❌ Forget about implementer experience
  • ❌ Profile in isolation without stakeholder input

Tools & Resources

Essential Tools

Learning Resources

Need Help with FHIR Profiling?

Our experts can help you design, validate, and implement FHIR profiles tailored to your organization's specific requirements.