Profile Immunization resource for COVID-19 vaccine use case

This page contains step by step exercises that are part of the 'Let's Build a FHIR specification track' at FHIR DevDays 2020 November.

These exercises teach you how to start profiling, the art of defining your FHIR data model, with Forge.

After completing this tutorial, you will be able to:

  • Create FHIR profiles, extensions and search parameters with Forge.
  • Model data properties like cardinalities and data types.
  • Create profile references and terminology bindings.
  • Slice lists of data elements.
  • Publish FHIR resources to Simplifier.net.

    Use case: COVID-19 vaccination and adverse events report šŸ’‰šŸ¤’

    In some fictional country, the medicine regulatory authority wants to monitor the uptake and safety of COVID-19 vaccines using a FHIR interface. Healthcare providers will expose COVID-19 vaccination administration and related adverse events.

    The medicine regulatory authority needs to express its required data model and use cases in FHIR. They need to publish their own FHIR specification which helps the healthcare providers to implement the interface. And this is where you come in! You are asked to author these specifications.

    This page zooms in on the creation of a profile on the Immunization resource for the expression of the medicine regulatory authorities' requirements.

  • A free Simpifier.net account (sign up here)
  • Forge for FHIR R4 (free for non-commercial use, download here)
  • If youā€™re on Windows: Double-click the installer and follow the process
  • If youā€™re on Mac or Linux: While Forge originally wasnā€™t built for these platforms, you can run Forge in a virtual machine with Windows or, in some case, with Wine.
  • Step 1: Basic Profiling šŸ”„

      The medicine regulatory authority (MRA) specifies the following requirements on top op the FHIR model:

      1. Information on the path by which the vaccine product is taken into the body needs to be present.
      2. The vaccine product that is administrated needs to be exchanged as a code and not solely in free text.
      3. If a reaction follows on the vaccination administration, the reaction datetime and details needs to be captured.
      4. The MRA cannot process information about given education material, therefore it should not be present.
      5. If a performer is known, the information about the actor (the individual Practitioner and it's Organization) must be given through a PractitionerRole reference.

    Step 1.1: Create an Immunization profile

    In this step, weā€™ll create a profile with some high-level metadata. Start by creating a new folder on your computer for this tutorial. Open Forge and open the profile folder you just made.

    Create a new profile and choose the base resource that is the best fit for vaccination administration data (the Immunization resource šŸ˜‰). Add the high-level metadata in the 'Properties' tab within Forge. Of course, you can deviate from the proposed values to make it truly your profile.

    Metadata Value
    URL http://<yourname>.<yourcountrycode >/fhir/StructureDefinition/<profile name> for example:
    http://ardon.nl/fhir/StructureDefinition/ImmunizationProfile
    Resource ID immunization-profile
    Name ImmunizationProfile
    Title Immunization Profile
    Description Defines the medicine regulatory authority constraints and extensions on the Immunization resource for the minimal set of data to query and retrieve an individualā€™s immunization information.

    From now on we will use the term ImmunizationProfile to refer to this profile.

    Step 1.2: Add cardinality and reference constraints

    Set the constraints according to the requirements of the medicine regulatory authority.

    Step 1.3: Publish to Simplifier.net

    Let's upload the patient profile to Simplifier.net and check if everything works as expected.

    • Create a Simplifier project (based on R4) if you haven't created one already: create one here
    • In Forge, go to File > Publish to Simplifier or hit Ctrl + U
      • Function greyed out? Make sure you have opened and selected the profile you want to upload in Forge.
    • Select your Simplifier project
      • Project not shown? Check that the project is configured based on the same FHIR version as your profile.
    • Click save and voila - your profile is now rendered online!

    If all went well you should see the profile in your project. You can confirm your profile constraints in the rendering as shown in the Overview tab of your profile. Selecting diff mode helps to pin down on the made changes. The below results box

    Differential view of ImmunizationProfile on Simplifier

    identifier0..*Identifier
    statusĪ£ ?!1..1codeBinding
    statusReason0..1CodeableConcept
    systemĪ£1..1uri
    versionĪ£0..1string
    codeĪ£1..1code
    displayĪ£1..1string
    userSelectedĪ£0..1boolean
    textĪ£0..1string
    patientĪ£ I1..1Reference(Patient)
    encounterI0..1Reference(Encounter)
    occurrenceDateTimedateTime
    occurrenceStringstring
    recorded0..1dateTime
    primarySourceĪ£0..1boolean
    reportOrigin0..1CodeableConcept
    locationI0..1Reference(Location)
    manufacturerI0..1Reference(Organization)
    lotNumber0..1string
    expirationDate0..1date
    site0..1CodeableConcept
    route1..1CodeableConcept
    doseQuantityI0..1SimpleQuantity
    functionĪ£0..1CodeableConceptBinding
    actorĪ£ I1..1Reference(PractitionerRole)
    noteĪ£0..*Annotation
    reasonCode0..*CodeableConcept
    reasonReferenceI0..*Reference(Condition | Observation | DiagnosticReport)
    isSubpotentĪ£ ?!0..1boolean
    subpotentReason0..*CodeableConcept
    programEligibility0..*CodeableConcept
    fundingSource0..1CodeableConcept
    date1..1dateTime
    detailI1..1Reference(Observation)
    reported0..1boolean
    series0..1string
    authorityI0..1Reference(Organization)
    targetDisease0..*CodeableConcept
    doseNumberPositiveIntpositiveInt
    doseNumberStringstring
    seriesDosesPositiveIntpositiveInt
    seriesDosesStringstring

    Step 2: Add extensions to ImmunizationProfile āž•

      Not all required information is present in the core Immunization resource. The following addition requirments are made.

      1. The medicine regulatory authority (MRA) needs to know the performed vaccination procedure.
      2. The MRA needs to have all the specific details of the administrated vaccine product. Although the vaccineCode and other relevant elements of Immunization provide essential information the Medication resource is more suitable for all the requirements.

    Step 2.1: Reuse an extension: ExtensionUKCoreVaccinationProcedure

    A search in the FHIR Registry and Simplifier.net revealed that HL7 UK published an extension to capture information about the vaccination procedure in a coded way. It is named ExtensionUKCoreVaccinationProcedure. We are going to reuse that extension.

    HL7 UK published the ExtensionUKCoreVaccinationProcedure in a FHIR package. That makes it even easier for us to include the extension.

    • Examine the extension and see where the extension is allowed to be used to see if it fits.
    • In Forge, select your profiling folder and go to the dependencies tab
    • Hit the Simplifier icon
    • Search for Uk.Core.r4
    • Install version 1.3.0 by selecting it and clicking on ā€˜Addā€™

    Nice! You have downloaded all the published HL7 UK profiles and it's dependencies (in this case also the HL7 FHIR R4 core resources) to your local package cache folder. The profiling folder now contains a package.json file with references to the package. In short, all those profiles are now ready to be used in Forge!

    • Add the extension to the ImmunizationProfile. Select your profile, go to the Element Tree view and select the Immunization root element. Click the extend button and find the extension.
    • Give the extension a proper (slice)name, like 'vaccinationProcedure'.
    • Make the extension mandatory.

    Step: 2.2: Create a simple extension and add it to ImmunizationProfile

    The Immunization resource does not contain a reference to the Medication resource. We are going to create our own extension to add this reference.

    • In Forge, select the profiling folder and click on New Extension. In the new screen that pops up, select 'Extension definition' in the left menu and use the 'profiles-types.xml'.
    • Similiar to the ImmunizationProfile, the extension needs at least some basic metadata such as a canonical URL, a name and description. See the table below for example values.
    • It is mandatory to provide the extension context. This element indicates where the extension is allowed to be used. You can set this in the 'Properties' tab of the extension. (Tip: the Context element is at the bottom.)
    Metadata value
    URL http://<yourname>.<yourcountrycode >/fhir/StructureDefinition/<profile name>
    Resource ID extension-administeredproduct
    Name ExtensionAdministeredProduct
    Title Vaccination Product Administered
    Description A reference to the Medication resouce to provide details about the product administered during a vaccination event.
    Context Type = 'element' and Expression = 'Immunization'
    • Profile the extension so that provides a reference element to a Medication resource.
    • Step by step: click value[x], uncheck all data types, check the Reference datatype and expand it. Here you can provide the Medication URL ('http://hl7.org/fhir/StructureDefinition/Medication') in the Target Profiles.
    • Add the extension to ImmunizationProfile, on a similar basis as with step 2.1

    Step 2.3: Publish the extension and the updated ImmunizationProfile to Simplifier.net

    Let's upload the extension and the updated ImmunizationProfile to Simplifier.net and check if everything works as expected.

    Likely Simplifier will complain that it cannot resolve ExtensionUKCoreVaccinationProcedure. For this, you should add a project dependency to the Uk.Core.r4 package. Go to the dependency tab, hit the green manage button, find and add the package.

    Differential view of ImmunizationProfile

    vaccinationProcedureI1..*Extension
    administeredProductI1..1Extension(Reference(Medication))
    identifier0..*Identifier
    statusĪ£ ?!1..1codeBinding
    statusReason0..1CodeableConcept
    systemĪ£1..1uri
    versionĪ£0..1string
    codeĪ£1..1code
    displayĪ£0..1string
    userSelectedĪ£0..1boolean
    textĪ£0..1string
    patientĪ£ I1..1Reference(Patient)
    encounterI0..1Reference(Encounter)
    occurrenceDateTimedateTime
    occurrenceStringstring
    recorded0..1dateTime
    primarySourceĪ£0..1boolean
    reportOrigin0..1CodeableConcept
    locationI0..1Reference(Location)
    manufacturerI0..1Reference(Organization)
    lotNumber0..1string
    expirationDate0..1date
    site0..1CodeableConcept
    route1..1CodeableConcept
    doseQuantityI0..1SimpleQuantity
    functionĪ£0..1CodeableConceptBinding
    actorĪ£ I1..1Reference(PractitionerRole)
    noteĪ£0..*Annotation
    reasonCode0..*CodeableConcept
    reasonReferenceI0..*Reference(Condition | Observation | DiagnosticReport)
    isSubpotentĪ£ ?!0..1boolean
    subpotentReason0..*CodeableConcept
    programEligibility0..*CodeableConcept
    fundingSource0..1CodeableConcept
    date1..1dateTime
    detailI1..1Reference(Observation)
    reported0..1boolean
    series0..1string
    authorityI0..1Reference(Organization)
    targetDisease0..*CodeableConcept
    doseNumberPositiveIntpositiveInt
    doseNumberStringstring
    seriesDosesPositiveIntpositiveInt
    seriesDosesStringstring

    Differential view of ExtensionAdministeredProduct

    url1..1System.StringFixed Value
    valueReferenceReference(Medication)

    Step 3: Terminology bindings šŸ’Ž

      The medicine regulatory authority (MRA) can only capture specific data on the certain elements. The following requirements are specified:

      1. For the vaccine administration route, the MRA uses the codes in the bound ValueSet by the FHIR core Immunization resource. A code from this ValuSet is exected.
      2. The FHIR ValueSet bound to site is not sufficiently detailed. The MRA can handle SNOMED codes and expects codes as defined by the International Patient Summary for target site.

    Step 3.1: Adjust the ValueSet binding strength in ImmunizationProfile

    • Adjust the ValueSet binding strength of the route element. Tip! The FHIR specification provides a thorough description of the meaning of the binding strenghts.

    Step 3.2: Find the right ValueSet and add it to ImmunizationProfile

    The International Patient Summary has defined a ValueSet for site named TargetSiteCodeUvIps.

    • Examine the ValueSet.
    • Copy the ValuSet canonical URL into the ValueSet binding in Forge.
    • Adjust the binding strength.

    You can download the ValueSet to include it in your profile folder or download it through the International Patient Summary package hl7.fhir.uv.ips in a similar way as step 2.4.

    Step 3.3: Publish the updated ImmunizationProfile to Simplifier.net

    Upload the updated ImmunizationProfile to Simplifier.net and check if everything works as expected. Remember, pressing CTRL + U pops up the publish screen. Then just select your project e voila!

    Optionally, you can add a project dependency to a specific version of the hl7.fhir.uv.ips package to let the ValueSet reference of step 3.2 resolve to a versioned version of the ValueSet.

    Differential view of ImmunizationProfile

    vaccinationProcedureI1..*Extension
    administeredProductI1..1Extension(Reference(Medication))
    identifier0..*Identifier
    statusĪ£ ?!1..1codeBinding
    statusReason0..1CodeableConcept
    systemĪ£1..1uri
    versionĪ£0..1string
    codeĪ£1..1code
    displayĪ£0..1string
    userSelectedĪ£0..1boolean
    textĪ£0..1string
    patientĪ£ I1..1Reference(Patient)
    encounterI0..1Reference(Encounter)
    occurrenceDateTimedateTime
    occurrenceStringstring
    recorded0..1dateTime
    primarySourceĪ£0..1boolean
    reportOrigin0..1CodeableConcept
    locationI0..1Reference(Location)
    manufacturerI0..1Reference(Organization)
    lotNumber0..1string
    expirationDate0..1date
    site0..1CodeableConceptBinding
    route1..1CodeableConceptBinding
    doseQuantityI0..1SimpleQuantity
    functionĪ£0..1CodeableConceptBinding
    actorĪ£ I1..1Reference(PractitionerRole)
    noteĪ£0..*Annotation
    reasonCode0..*CodeableConcept
    reasonReferenceI0..*Reference(Condition | Observation | DiagnosticReport)
    isSubpotentĪ£ ?!0..1boolean
    subpotentReason0..*CodeableConcept
    programEligibility0..*CodeableConcept
    fundingSource0..1CodeableConcept
    date1..1dateTime
    detailI1..1Reference(Observation)
    reported0..1boolean
    series0..1string
    authorityI0..1Reference(Organization)
    targetDisease0..*CodeableConcept
    doseNumberPositiveIntpositiveInt
    doseNumberStringstring
    seriesDosesPositiveIntpositiveInt
    seriesDosesStringstring

    Step 4: Slicing āœ‚ļøšŸ•

      The medicine regulatory authority (MRA) has additinoal requirments that require a little bit more advance profiling. The following requirements are made:

      1. The MRA has specific implementation comments for the use of the String and DateTime datatypes of the vaccine administration date.
      2. Only coded values from SNOMED as defined in the International Patient Summery VaccinesGpsUvIpsv ValueSet or WHO ATC codes can be used to indicate the administrated vaccine product.

    Slicing is useful when constraints need to be made that do not necessarily apply to the whole element. In this case, it would be best to slice the element first and then apply constraints to the slices individually. This allows you to define different constraints to each slice of a single element, thereby further customizing your profile.

    Step 4.1: Create a type slice for occurrence[x]

    A common form of slicing is so-called type slices. FHIR resources often contain polymorphic elements which are indicated with the [x] at the end (i.e. Observation.value[X] and the Immunization.occurcence[X]).

    In this step, we are going to place a comment in both the slice for DateTime and the slice for String. You could consider other constraints as well, such as indicating a maximum string length for String or a minimum date for DateTime.

    • First, we have to slice the occurrence[x] element by selecting it and clicking the Slice button on top.
    • Add two slices with the button next to it.
    • Forge throws errors at you now because the slices don't have a name and it's own type. Let's solve that by specializing each slice with a specific type. Forge will then automatically assign the slice names occurrenceDateTime en occurrenceString to the slice.
    • Note that Forge reconizes that this is a type slice and prefils the slicing details in the occurrence[x] element with Type = 'type' and Path = '$this'.
    • Finally, add implementation comments for the slices. For example, the String data type should only be used in the rare event that the DateTime is not known with of patient self-administered dose. Or you can be creative and add other constraints.

    Note: this isn't the best example of a type slice because the implementation comments we are adding could have been made clear on the occurrence[x] element without slicing. It does however simulate how to profile an element in multiple ways.

    Step 4.2: Bind multiple ValueSets to vaccineCode

    The International Patient Summary defines the following two ValueSets that we will reuse in the ImmunizationProfile: VaccinesGpsUvIps and WhoAtcUvIps. As slicing can only be done on repeating elements (or polymorphic elements), we will slice the vaccineCode.coding because coding is 0..*.

    • Remove the previous made cardinality constraints so the pencil icon disappears.
    • Perform the same procedure as in step 4.1 (create two slices).
    • Provide the slices with the names of ValueSets
    • Let's get rid of the error by providing the right slicing details at vaccineCode.coding. This time we are creating a slicing definition based on the binding ValueSets. In other words, the slices are discriminated by the codes inside bound ValueSets. We should let the validator logic know how it can discriminate between the slices by providing the slicing details: Type = 'value' and Path = '$this'. Forge does not show the $this option in the dropdown, but you can type it in. The equivalent for $this is the element you are in, this case coding. The FHIR spec provides a lot of information on slice discriminators.
    • Next, we will add the ValueSet bindings to the sliced elements. Provide the ValueSet's URL in the ValueSet reference box.
    • Set the binding strength to 'required'. This is important for slices based on bound ValueSets. Without the required strength, the validator cannot validate the instance because it cannot confirm if the provided code belongs to one of the bound ValueSets.
    • Lastly, set the cardinality of vaccineCode.coding to 1..* so at least a coded value of one of these ValueSets needs to be given.

    Step 4.3: Publish the updated ImmunizationProfile to Simplifier.net

    This step should be familiar by now! šŸ˜Š Update your profile and compare it with the results here below.

    Differential view of ImmunizationProfile

    vaccinationProcedureI1..*Extension
    administeredProductI1..1Extension(Reference(Medication))
    identifier0..*Identifier
    statusĪ£ ?!1..1codeBinding
    statusReason0..1CodeableConcept
    systemĪ£1..1uri
    versionĪ£0..1string
    codeĪ£1..1code
    displayĪ£0..1string
    userSelectedĪ£0..1boolean
    systemĪ£1..1uri
    versionĪ£0..1string
    codeĪ£1..1code
    displayĪ£0..1string
    userSelectedĪ£0..1boolean
    textĪ£0..1string
    patientĪ£ I1..1Reference(Patient)
    encounterI0..1Reference(Encounter)
    occurrenceDateTimeĪ£0..1dateTime
    occurrenceStringĪ£0..1string
    recorded0..1dateTime
    primarySourceĪ£0..1boolean
    reportOrigin0..1CodeableConcept
    locationI0..1Reference(Location)
    manufacturerI0..1Reference(Organization)
    lotNumber0..1string
    expirationDate0..1date
    site0..1CodeableConceptBinding
    route1..1CodeableConceptBinding
    doseQuantityI0..1SimpleQuantity
    functionĪ£0..1CodeableConceptBinding
    actorĪ£ I1..1Reference(PractitionerRole)
    noteĪ£0..*Annotation
    reasonCode0..*CodeableConcept
    reasonReferenceI0..*Reference(Condition | Observation | DiagnosticReport)
    isSubpotentĪ£ ?!0..1boolean
    subpotentReason0..*CodeableConcept
    programEligibility0..*CodeableConcept
    fundingSource0..1CodeableConcept
    date1..1dateTime
    detailI1..1Reference(Observation)
    reported0..1boolean
    series0..1string
    authorityI0..1Reference(Organization)
    targetDisease0..*CodeableConcept
    doseNumberPositiveIntpositiveInt
    doseNumberStringstring
    seriesDosesPositiveIntpositiveInt
    seriesDosesStringstring

    Step 5: Create a SearchParameter definition šŸ”Ž

      In the current design, the medicine regulatory authority (MRA) retrieves the vaccination information from the healthcare provider's system. The MRA wants to search administered vaccinations resources based on the detailed information of the administered product. Additionally, this allows to include the administered product in the search results without the need to perform an additional read request.

    Step 5.1: Create a SearchParameter resource with basic metadata

    We created an extension named 'ExtensionAdministeredProduct' in step 2.2. The FHIR specification defines SearchParameter resources for elements that are included in common use cases. These search parameters are listed at the bottom of every resource page. Here is the list of SearchParameters for Immunization.

    Because we added an extension, there is no SearchParameter available in the FHIR core specification. That's not a problem as we can create it ourselves with Forge! Almost all FHIR reference servers can incorporate SearchParameters, some even on the fly.

    • Select your profile folder in Forge, hit the little dropdown button next to New and select New Search Parameter.
    • Forge throws a lot of warnings. Don't worry, we will get rit of them all. Start with filling in basic metadata in the properties tab:
    Metadata Value
    URL http://<yourname>.<yourcountrycode >/fhir/SearchParameter/<searchparameter name>
    Resource ID searchparameter-administeredproduct
    Name SearchParameterAdministeredProduct
    Description To search (and _include) Medication resources that are referenced from the AdministeredProduct extension in Immunization.

    Step 5.2: Provide the SearchParameter details

    Continue with the required information in the SearchParameter tab.

    • Provide a Code. This code is used in the API Search URLs and should not overlap one of the existing SearchParamater codes for Immunization.
    • Click on the blue plus sign at Base to indicate the base resources this parameter applies.
    • State the data type of SearchParameter value in the dropdown at Type.
    • Last but certainly not least: Expression. The SearchParameter needs precisely states what to search on. Provide the following FHIRPath expression to search on the previously created extension:
      Immunization.extension('[extension URL]')

    That's it! You have created your own SearchParameter to extend the core FHIR API. You can test if it works by creating instances of the FHIR profiles, import all the conformance resources and play around with a HTTP tool such as Postman. This is however out of scope for this exercise. There is only one step left!

    Step 5.3: Publish the SearchParameter to Simplifier.net

    This is the last step! Well done. Compare your results with the SearchParameter here below.

    JSON view of SearchParameter AdministeredProduct

    {
        "resourceType": "SearchParameter",
        "id": "searchparameter-administeredproduct",
        "meta": {
            "lastUpdated": "2020-11-15T20:48:14.822+00:00"
        },
        "url": "https://fhir.devdays.com/R4/SearchParameter/AdministeredProduct",
        "name": "SearchParameterAdministeredProduct",
        "status": "draft",
        "date": "2020-11-15T20:48:14.6108237+00:00",
        "description": "To search (and _include) Medication resources that are referenced from the AdministeredProduct extension in Immunization.",
        "purpose": "To allow to search on Medication resource, referenced from a Immunization.extension.administeredProduct extension. Main purpose is to use this search parameter for including related Medication resources in the result for a search on Immunization.",
        "code": "administered-product",
        "base":  [
            "Immunization"
        ],
        "type": "reference",
        "expression": "Immunization.extension('https://fhir.devdays.com/R4/StructureDefinition/Extension-AdministeredProduct')"
    }