Meet FSH: The Language That Makes FHIR Profiles Bearable
If you've ever opened a FHIR StructureDefinition JSON, you've probably had one of two reactions:
- "Wow, that's... verbose."
- "There has to be a better way."
There is. It's called FHIR Shorthand (FSH).
Despite its fishy name, FSH isn't another interoperability standard. It doesn't replace FHIR, and it doesn't change how systems exchange data. It's simply a language designed to make authoring FHIR artifacts pleasant for humans instead of machines.
Instead of editing thousands of lines of JSON, you write concise, readable definitions that describe your intent. A compiler called SUSHI then translates your FSH files into the FHIR JSON artifacts required by the Implementation Guide Publisher.
Why was FSH created?
FHIR is designed for computers. Implementation Guides are written by humans. Those are very different audiences.
Take a simple requirement:
Every patient in my country must have a national identifier.
Expressing that as constraints inside a StructureDefinition JSON quickly becomes difficult to read and maintain. With FSH, the same idea becomes something much closer to documentation than programming:
Profile: MyPatient
Parent: Patient
* identifier 1..*
* identifier.system = "https://mycountry.gov/patient-id"
You focus on what you want to constrain, not on the mechanics of the underlying JSON.
What can you create with FSH?
Most people associate FSH with profiles, but it can define almost every artifact involved in an Implementation Guide:
- Profiles
- Extensions
- Logical Models
- Value Sets
- Code Systems
- Instances
- Invariants
- Mappings
If you're building an Implementation Guide, chances are you'll spend most of your time writing FSH rather than editing JSON directly.
The FSH ecosystem
One thing that confused me when I first started was understanding where FSH fits in the FHIR toolchain. It's easier to think of it as an authoring workflow:
FSH is not another interoperability standard. It's simply the language you use while writing an Implementation Guide.
Your first profile
Let's create the smallest possible profile:
Profile: SimplePatient
Parent: Patient
* active = true
That's it. Running SUSHI generates a valid StructureDefinition representing the same constraint as a FHIR profile. SUSHI is the reference compiler that transforms FSH into the JSON artifacts consumed by the rest of the FHIR publishing toolchain.
Adding constraints
The real power of FSH appears as your profiles grow. Instead of editing nested JSON structures, constraints remain concise and readable.
For example, you can:
- Change cardinalities
- Fix values
- Restrict datatypes
- Bind ValueSets
- Define slices
- Create invariants
- Reference extensions
Each of these can usually be expressed in a single line instead of dozens of JSON elements.
Creating extensions
Profiles constrain existing resources. Extensions add information that isn't part of the base FHIR specification. Fortunately, the syntax is very similar:
Extension: Nationality
Id: nationality
* value[x] only CodeableConcept
Once compiled, this becomes a normal FHIR StructureDefinition representing an Extension.
From FSH to JSON with SUSHI
Once you've written your .fsh files, generating the FHIR artifacts is straightforward:
sushi .
SUSHI reads the FSH project, processes every definition and produces the corresponding FHIR resources as JSON. It also integrates naturally with the HL7 IG Publisher, which generates the final Implementation Guide website.
You can also initialize a new project with:
sushi init
This creates the recommended project structure, including the input/fsh directory where your FSH files live and the configuration file needed for compilation.
Tips from experience
Rather than learning every FSH keyword first, learn the workflow. Understand these four pieces:
- FHIR Resources
- Profiles
- FSH
- SUSHI
Once those concepts click, the language itself is surprisingly small. I'd also recommend:
- Visual Studio Code with the FSH extension
- Git for version control
- One
.fshfile per profile or extension - Aliases from the beginning — they'll save you a lot of typing
- RuleSets once you notice you're repeating the same constraints across multiple profiles
What's next?
In the next guides we'll look at:
- Building a complete FSH project
- Profiling Observations
- Writing reusable extensions
- ValueSets and terminology bindings
- Slicing without losing your sanity
- Publishing an Implementation Guide
