Track Leads: Mark Kramer and Chris Moesel
During this Let’s Build! session, you will learn how to build a small FHIR Implementation Guide (IG) by attempting to reproduce the US Core Patient profile using FHIR Shorthand.
After completing this tutorial, you will be able to:
- Install SUSHI, the reference implementation FHIR Shorthand compiler
- Define basic profiles, extensions, and value sets using FHIR Shorthand
- Build a human-readable FHIR IG using SUSHI and the HL7 FHIR IG Publisher
- A text editor (recommended: VS Code with vscode-language-fsh extension)
- Node.js LTS – needed to install and run SUSHI
- SUSHI – needed to compile FSH into valid FHIR definitions
- OpenJDK 8 (or licensed Oracle JDK) – needed to run HL7 IG Publisher
- Ruby and Jekyll – needed to run HL7 IG Publisher
- FHIR Shorthand May 2020 ballot specification
- US Core STU 3.1 Patient profile
- FSH Let’s Build Starter Project
In this exercise, we will review the US Core Patient profile and attempt to recreate it using FHIR Shorthand. Novice IG authors may choose to implement only part of the profile, while braver souls (with lots of free time on their hands) can attempt to implement the entire profile, including the extensions and value sets that it references. In all cases (novice or expert), IG authors will build a minimal Implementation Guide that allows them to view their profile in a web browser.
NOTE: This exercise is designed to get progressively more complicated. We recommend all participants attempt to complete at least the first seven steps. Steps eight and above are there for those who like to squeeze every last penny out of their DevDays registration fee!
First, install the required software listed in the "Requirements" section above. Follow the links to find the download packages and/or installation instructions.
Ensure that your version of SUSHI is 0.13.0 or later. Earlier versions will not work! To check:
- Open a command prompt using Command Prompt on Windows or Terminal on Mac.
- Run the command:
sushi -v
If you did not have an opportunity to attend the FHIR Shorthand tutorial, read the FHIR Shorthand Overview to get a basic understanding of FHIR Shorthand.
Download the FSH Let’s Build Starter Project and unzip it to a location of your choice. This project provides a common project structure for FSH projects, allowing you to get started right away! You didn't really want to start from scratch, did you?
Open this folder in your favorite text editor (we recommend VS Code if you don’t have one) or file explorer (if your favorite text editor cannot open folders). The starter project contains the following file structure:
.
├── .gitignore // Ignores generated build artifacts in git so you don't check them in
├── README.md // The file you are reading now (!)
├── _genonce.bat // Win: Runs the IG Publisher on your IG
├── _genonce.sh // Mac: Runs the IG Publisher on your IG
├── _updatePublisher.bat // Win: Downloads and/or updates your IG Publisher jar
├── _updatePublisher.sh // Mac: Downloads and/or updates your IG Publisher jar
└── fsh
├── config.yaml // The SUSHI config file, pre-configured for US Core Patient IG
├── ig-data
│ └── input
│ └── pagecontent
│ └── index.md // Markdown file providing the contents of your IG's home page
└── patient.fsh // The FSH file where you will write your FSH definitions
Open the US Core Patient profile documentation in your web browser. Scroll down to the Formal Views of Profile Content section and click on the Differential View tab. This tab shows how the US Core Patient profile differs from the base FHIR R4 Patient resource. Review this tab to become familiar with the differences, such as the extensions, cardinality constraints, "must support" flags, value set bindings, and invariants (e.g., us-core-8). You will need to come back to this view as you build your profile in FHIR Shorthand.
Now click on the JSON tab at the top of the page to see the JSON StructureDefinition representation of this profile. Briefly thank your lucky stars that you get to write FHIR Shorthand instead of having to write this file. Once you feel appropriately grateful, use your browser to search for the word "differential" in the JSON definition. The JSON contents below the word "differential" are the computer-friendly representation of the same differences that you just viewed in the human-friendly Differential View tab. Hopefully you won’t need to look at this again (but you might).
In this step, we’ll create a USCorePatientProfile definition with some high-level metadata. Start by opening the file fsh/patient.fsh in your text editor. Now, create a profile with the following high-level metadata:
Metadata | Value |
---|---|
Name | USCorePatientProfile |
Parent | Patient |
Id | us-core-patient |
Title | US Core Patient Profile |
Description | Defines constraints and extensions on the patient resource for the minimal set of data to query and retrieve patient demographic information. |
New to FHIR Shorthand? See Defining Profiles in the FHIR Shorthand specification for help. Look at the example (you can thank us later). While that example provides all of the keywords (and more!) that you’ll need for this step, you can also reference the Keywords section to see all of the available keywords.
Think you nailed Step 5? Great! Now it's time to prove it! Follow these steps to run SUSHI on your FSH project:
- Open a command prompt using Command Prompt on Windows or Terminal on Mac.
- Change directories to the unzipped
fsh-devdays-exercise-0.0.1
folder (commandline newbies: Win / Mac) - Run the following command:
sushi
- If you haven’t installed SUSHI yet, see the links at the top of this document
If you wrote valid FHIR Shorthand, SUSHI will exit reporting 1 profile with 0 errors and 0 warnings. You also get a free random fish pun to celebrate with!
╔════════════════════════ SUSHI RESULTS ══════════════════════════╗
║ ╭──────────┬────────────┬───────────┬─────────────┬───────────╮ ║
║ │ Profiles │ Extensions │ ValueSets │ CodeSystems │ Instances │ ║
║ ├──────────┼────────────┼───────────┼─────────────┼───────────┤ ║
║ │ 1 │ 0 │ 0 │ 0 │ 0 │ ║
║ ╰──────────┴────────────┴───────────┴─────────────┴───────────╯ ║
║ ║
║ See SUSHI-GENERATED-FILES.md for details on generated IG files. ║
╠═════════════════════════════════════════════════════════════════╣
║ O-fish-ally error free! 0 Errors 0 Warnings ║
╚═════════════════════════════════════════════════════════════════╝
If you got any errors, they'll be reflected in the log and counted in the summary. You will also get a random fish pun as pun-ishment (see what we did there?). Go back to your text editor, fix your FSH definition, and try again!
When the build is successful, go ahead and take a look at the new "input" folder in your project. This contains the files that SUSHI generated for you. It is (confusingly) called "input" because it is input to the HL7 FHIR IG Publisher. So... let’s try running the IG Publisher now!
To run the HL7 IG Publisher on the files that SUSHI just generated:
- Go back to your command prompt (which should still be in your unzipped project directory)
- Run the following command to download the HL7 IG Publisher jar (Java Archive)
- Windows:
_updatePublisher.bat
- Mac:
./_updatePublisher.sh
- Windows:
- Once it is downloaded, run the following command to invoke the HL7 IG Publisher:
- Windows:
_genonce.bat
- Mac:
./_genonce.sh
- If you haven’t installed Java yet, see the links at the top of this document
- Windows:
If the IG Publisher completed successfully, you should now be able to view your human-readable Implementation Guide by opening the file at output/index.html in your web browser. Click "Artifacts" in the menu, then click on the link for your USCorePatientProfile. Since we haven’t authored any constraints yet, this profile won’t be very exciting, but we’re on our way to bigger and better things!
NOTE: Throughout this exercise, you may notice slight differences in how your profile is visually rendered compared to the one in US Core. This is expected because we are using a different template than US Core and the IG Publisher has also changed how it renders several things since US Core was last published.
NOTE 2: You also might notice that the IG Publisher runs SUSHI too! That's OK -- and in fact, it's quite helpful when you're using the Auto IG Builder. If the redundancy really bothers you, then... we're sorry. Hopefully you'll find that learning to live with it builds character. There is a secret to make it stop, but you'll need to find it on your own in the Zulip jungle.
Review the Differential View in the US Core Patient profile. Note the Flags and Card. columns. The red S in the Flags column indicates "must support". Update your US Core Patient profile FSH definition with rules constraining the cardinality and applying "must support" flags as seen in the Differential View. For now, ignore the first three extensions (indicated by a green icon).
We'll give you one rule to get started:
* identifier 1..* MS
You may want to consult the following sections of the FHIR Shorthand specification for more help:
When you’re done, run SUSHI and the IG Publisher again (Step 6) and review the output, paying special attention to cardinality and flags in your profile’s Differential View.
NOTE: You may notice that some of your cardinalities are in a gray font; this is because they do not actually differ from the base Patient resource. Unfortunately, the US Core Patient profile rendering does not properly show this – but now that you know which ones aren’t really different, you can remove those cardinality constraints from your FSH if you wish! Go ahead and do it; deleting code feels great!
If you made it this far, pat yourself on the back -- you now know enough about SUSHI and FHIR Shorthand to capture (and then lose) your friends' interest at the next cocktail party! If you want to stop here, you can walk away feeling good about what you've done. If you want to keep going, we're about to dive into some more fun waters with value sets and extensions!
Review the Differential View in the US Core Patient profile and note the "Binding" on address.state. A value set binding provides a set of codes that are valid for a given element. Click on the binding link: USPS Two Letter Alphabetic Codes.
Your goal for this step of the exercise is to create a miniature version of this value set with only a few codes (because who has time to write 50+ of these?).
Like we did for patient, start off by creating the basic ValueSet definition with the following metadata:
Metadata | Value |
---|---|
Name | UspsTwoLetterAlphabeticCodes |
Id | us-core-usps-state |
Title | USPS Two Letter Alphabetic Codes |
Description | This value set defines two letter USPS alphabetic codes. |
Need some help? Check out Defining Value Sets in the FHIR Shorthand specification.
Next, specify a small set of codes as members of the value set. Since New England is obviously the
best region of the United States, we'll specify the codes for ME
, VT
, NH
, MA
, RI
, and
CT
.
HINT: To make this easier (and save some typing), you'll probably want to define an alias for the USPS code system:
https://www.usps.com
.
You may want to consult the following sections of the FHIR Shorthand specification for more help:
Now run SUSHI again and fix any reported errors. Once SUSHI succeeds, run the IG Publisher again and reload your IG in your web browser. Click on the "Artifacts" menu item and notice that the USPS Two Letter Alphabetic Codes value set is now listed. Click it to review its contents.
NOTE: You will likely see an error in the Expansion section of your value set page saying: "No Expansion for this valueset (not supported by Publication Tooling)". This is expected, as the IG Publisher does not formally support the USPS code system. Please do not blame SUSHI. SUSHI is sad too when this happens.
Now that you've defined the UspsTwoLetterAlphabeticCodes value set, bind it to the address.state element in your USCorePatientProfile definition using the binding strength: extensible.
You may want to consult the following section of the FHIR Shorthand specification for more help:
By now you know the drill: run SUSHI, fix errors as necessary, run the IG Publisher, and check out the output in your browser. If it works, you should see the UspsTwoLetterAlphabeticCodes binding in your USCorePatientProfile!
It's tough to write a robust implementation guide without needing to create at least one extension. The US Core Patient profile uses three! In this step, we'll create the simplest of the three extensions: USCoreBirthSexExtension.
Review the Differential View in the
US Core Patient profile
and note the third extension element: us-core-birthsex
. Click on the extension URL
http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex
to view the extension definition.
Creating an extension is very similar to creating a profile, but in this case, you're actually
profiling the Extension type, so there
are only a few fields to keep track of, primarily: value[x]
and extension
. Start off by creating
the basic Extension definition with the following metadata:
Metadata | Value |
---|---|
Name | USCoreBirthSexExtension |
Id | us-core-birthsex |
Title | US Core Birth Sex Extension |
Description | A code classifying the person's sex assigned at birth as specified by the [Office of the National Coordinator for Health IT (ONC)](https://www.healthit.gov/newsroom/about-onc). This extension aligns with the C-CDA Birth Sex Observation (LOINC 76689-9). |
If you need help, take a look at Defining Extensions in the FHIR Shorthand specification.
In FHIR, extensions can be simple or complex. The USCoreBirthSexExtension is a simple extension.
Simple extensions allow for a single value by constraining the value[x]
field to a particular type
(or types), optionally specifying additional constraints on the value[x]
type(s).
To complete this step of the exercise, create a rule that constrains USCoreBirthSexExtension's
value[x]
element to only allow the type: code
. Then create a new
BirthSex value set and bind
USCoreBirthSexExtension's value to it using the binding strength:
required.
You've created and bound value sets before (easy peasy!), but this step requires some new tricks: constraining choices to specific types and specifying FSH paths for specific choice types. You may want to consult the following sections of the FHIR Shorthand specification for help:
As usual, use SUSHI and the IG Publisher to check your work.
Now that you have that fancy new extension, it's time to use it! Go back to your USCorePatientProfile and constrain the extension element to indicate that it should contain an optional USCoreBirthSexExtension with the local name birthsex.
You may want to consult the following section of the FHIR Shorthand specification for more help:
- Extension Rules
- WARNING: The example for adding a standalone extension defined in the same FSH tank has a bug; it fails to provide a local name for the extension. See the other examples for guidance on how to do that. (Sorry, no one's perfect; not even us).
Now, go the extra step and flag that extension as "must support" in USCorePatientProfile. Once you think you've got it right, use SUSHI and the IG Publisher to test it out.
NOTE: We don't want to bore you too much with the details, but in case you're interested, adding extensions is a special case of slicing in FHIR. If you thought slicing was only for ninjas, well... you are now a ninja!
You've worked hard to get this far, so we thought we'd lob you a softball for this next one. You may have noticed that a couple of elements in the Differential View of the US Core Patient profile have a short description in the right-hand column. If you didn't notice, go look again.
Profile authors can affect what is displayed in the right-hand column of an element by providing a
short description in that element's metadata within the StructureDefiniton. (By the way, have we
told you yet that the formal specification format for resources, profiles, and extensions is called
a StructureDefinition? If not, now you know -- another cool tidbit to remember for those epic
cocktail party conversations!). In FHIR Shorthand, you can use the caret (^
) to set a metadata
value directly in the StructureDefinition.
For this step, use the special ^
syntax to set the short
metadata value for these elements in
the USCorePatientProfile:
Element | Short |
---|---|
identifier.value | The value that is unique within the system. |
address.postalCode | US Zip Codes |
You may want to consult the following section of the FHIR Shorthand specification for help:
If you're one of those curious people, take a look at the elements in
ElementDefinition. These are the paths you can
access after the ^
when setting metadata directly on an element in a profile.
NOTE: You can also use
^
to set high-level metadata in the StructureDefinition, like publisher, contact, and jurisdiction. Lucky for you, you don't need to do that here because we set that information globally in the config.yaml file and used special parameters to apply those values to all definitions in the IG. Check out that config.yaml file. Snazzy!
At some point, every good parent takes the training wheels off the bike, takes a deep breath, and gives their kid a shove. This is that moment for us. You've proven you can do this, so now you're on your own. If you're up to the challenge, finish this profile:
-
Create the USCoreRaceExtension and its associated value sets
-
Create the USCoreEthnicityExtension and its associated value sets
-
Create the LanguageCodesWithLanguageAndOptionallyARegionModifier value set and bind it to communication.language with binding strength:
extensible
-
Create the us-core-8 invariant with the following metadata and apply it to name:
Metadata Value Name us-core-8 Severity #error Description Patient.name.given or Patient.name.family or both SHALL be present Expression family.exists() or given.exists() XPath f:given or f:family
To get to the finish line, you may want to consult the following sections in the FHIR Shorthand specification:
- Defining Extensions
- Extension Rules
- Extension Paths
- Defining Value Sets
- Defining Invariants
- Invariant Rules
There's still more you can do to achieve complete parity with the US Core Patient profile (such as defining Mappings), but honestly... haven't you had enough? I'm starting to think you need a hobby.