FSH 101: Quick Start Guide for Polaris Developers¶
Welcome, Polaris developer! You're about to dive into FHIR Shorthand (FSH), the language we use to define our Polaris FHIR specifications. FSH makes it much easier to create and manage FHIR profiles and other artifacts compared to editing raw JSON. This guide will get you started with the basics.
Prerequisite: A general understanding of core FHIR concepts (Resources, Profiles, Elements). If you're new to FHIR, please read the "FHIR 101 for Polaris Developers" guide first.
What is FSH and Why Do We Use It?¶
- FSH (FHIR Shorthand): A domain-specific language for defining FHIR conformance resources like Profiles, Extensions, ValueSets, and CodeSystems.
- Concise & Readable: It's designed to be human-friendly and much less verbose than the equivalent FHIR JSON (
StructureDefinitionresources). - SUSHI: The command-line tool (compiler) that takes your FSH files (
.fsh) as input and generates the official FHIR JSONStructureDefinitionfiles. These JSON files are then used by the IG Publisher to build our Implementation Guide. - Polaris Standard: All Polaris FHIR specifications are authored in FSH.
Core FSH Syntax Elements (with Polaris Examples)¶
FSH files are plain text files, typically with a .fsh extension. Let's look at the common building blocks you'll see in Polaris FSH files (mostly found in src/fsh/resources/).
1. Comments¶
Just like in many programming languages:
// This is a single-line comment.
/*
This is a
multi-line comment.
*/
// Polaris<ResourceType>.fsh
Example: // PolarisCorePatient.fsh
2. Aliases¶
Aliases are shortcuts for long URLs or OIDs, making FSH more readable and maintainable. They are usually defined at the top of an FSH file or in a shared file like src/fsh/aliases.fsh.
- Syntax:
Alias: $<AliasName> = <URL or OID> - Polaris Convention: Alias names start with a
$.
// Defines an alias for the SNOMED CT code system URL
Alias: $SCT = http://snomed.info/sct
// Defines an alias for the PolarisCorePatient profile's canonical URL
Alias: $PolarisCorePatient = https://fhir.apps.health/StructureDefinition/polaris-core-patient
$SCT) instead of the full URL in your rules.
3. Defining a Profile¶
A Profile constrains a base FHIR Resource. This is the most common thing you'll see.
-
Syntax:
Profile: <ProfileName> Parent: <BaseResourceName or ParentProfileName> Id: <profile-id-kebab-case> Title: "<Human Readable Title>" Description: "<Detailed description, often multi-line using triple quotes>" // Other metadata like ^url, ^status // Rules follow... -
Polaris Example (
PolarisCorePatient.fshexcerpt):TheProfile: PolarisCorePatient Parent: Patient // Based on the core Patient resource Id: polaris-core-patient // kebab-case, used in URLs Title: "Polaris Patient" Description: "Generic Polaris core profile for patient demographic and administrative information..." * ^url = $PolarisCorePatient // Sets the canonical URL using an alias * ^status = #draft // All Polaris profiles start as draft* ^url = ...and* ^status = ...are "caret path" rules, setting metadata on theStructureDefinitionitself.
4. Rules: Constraining Elements¶
Rules are the heart of FSH. They start with an asterisk * and specify constraints on elements.
- Basic Structure:
* <elementPath> <constraint1> <constraint2> ...
a. Cardinality Rules¶
Defines how many times an element can appear (min..max).
- Syntax:
* <elementPath> <min>..<max>(*means unbounded) - Polaris Examples:
// Patient must have 1 or more identifiers * identifier 1..* // Patient can have 0 or 1 active status * active 0..1 // Patient must have exactly one gender * gender 1..1
b. Data Type Rules (only)¶
Restricts an element to specific data type(s). Especially useful for choice elements (like onset[x]).
- Syntax:
* <elementPath> only <DataType1> or <DataType2> ... - Polaris Example (
PolarisCoreObservation.fsh):// The effective[x] element can be either a dateTime or a Period * effective[x] only dateTime or Period
c. ValueSet Binding Rules (from)¶
Specifies that a coded element's value must come from a particular ValueSet.
- Syntax:
* <elementPath> from <ValueSetURI or $Alias> (<strength>)- Strength can be
required,extensible,preferred,example. Polaris typically usesrequiredorextensible.
- Strength can be
- Polaris Example (
PolarisCorePatient.fsh):// The gender element must be a code from the administrative-gender ValueSet * gender from http://hl7.org/fhir/ValueSet/administrative-gender (required)
d. Fixed Value Rules (=)¶
Assigns a fixed value to an element. In a Profile, this means instances must have this value.
- Syntax:
* <elementPath> = <value>- For codes:
* <elementPath> = #codeValue - For Codings:
* <elementPath> = $SystemAlias#codeValue "<Display Text>"
- For codes:
- Polaris Example (
PolarisCoreMedicationRequest.fsh):// The intent element must always be 'order' * intent = #order (exactly) // (exactly) means no other sub-elements allowed
e. Must Support Flag (MS)¶
Indicates an element is "Must Support" (implementers must be able to process it).
- Syntax:
* <elementPath> MS - Often combined with cardinality:
* identifier 1..* MS
f. Referencing Other Profiles (Reference())¶
Constrains a Reference element to point to resources conforming to specific profiles.
- Syntax:
* <elementPath> only Reference(<ProfileName1> or <ProfileName2>) - Polaris Example (
PolarisCoreCondition.fsh):// The subject of a PolarisCoreCondition must be a PolarisCorePatient * subject only Reference(PolarisCorePatient)
5. Invariants¶
Custom validation rules expressed using FHIRPath.
- Syntax:
Invariant: <invariant-id-kebab-case> Description: "<Human-readable description>" Severity: #error // or #warning Expression: "<FHIRPath expression that evaluates to true if valid>" - Applied to a profile or element using an
obeysrule:* obeys <invariant-id> - Polaris Example (common pattern):
Invariant: patient-has-polaris-id Description: "Must have at least one identifier using Polaris canonical NamingSystem." Severity: #error Expression: "identifier.exists(system.matches('^https://fhir.apps.health/NamingSystem/[a-zA-Z0-9_]+-patient-identifier$'))" // In PolarisCorePatient.fsh: Profile: PolarisCorePatient // ... * obeys patient-has-polaris-id // Applies the invariant to the whole profile
6. Defining ValueSets¶
Specifies a set of allowed codes.
- Syntax:
ValueSet: <ValueSetName> Id: <valueset-id-kebab-case> Title: "<Human Readable Title>" Description: "<Detailed description>" * include codes from system <SystemURI or $Alias> // Includes all codes * include codes from system $SCT where concept is-a #<code> // Filtered include * $SCT#<code> "<Display>" // Includes a specific code - Polaris Example (
PolarisCoreCondition.fsh):Alias: $VS_PolarisConditionCodes = https://fhir.apps.health/ValueSet/polaris-core-condition-codes ValueSet: PolarisConditionCodesVS Title: "Polaris Condition Codes" Description: "ValueSet for condition codes used in Polaris." * ^url = $VS_PolarisConditionCodes * ^status = #draft * include codes from system $SCT // Includes all SNOMED CT codes (very broad, usually more specific)
7. Defining Extensions¶
Creating new elements not present in base FHIR.
- Syntax:
Extension: <ExtensionName> Id: <extension-id-kebab-case> Title: "<Human Readable Title>" Description: "<Detailed description>" // Rules to define the extension's structure, often constraining value[x] * value[x] only <DataType> * value[x] from <ValueSetURI> (if coded) - Polaris Example (
PolarisCoreAppState.fsh):Extension: AppStateStringValue Id: app-state-string-value Title: "App State String Value Extension" Description: "A simple extension to hold the value of a Polaris application-state entry as a string." * ^url = "https://fhir.apps.health/StructureDefinition/app-state-string-value" // Set canonical URL * valueString 1..1 MS // Extension holds a single, mandatory string value
How FSH Relates to JSON Examples¶
The FSH rules you define for a Profile directly dictate the expected structure and constraints for any JSON instance claiming to conform to that Profile.
Example:
If PolarisCorePatient.fsh has:
* identifier 1..* MS // Must have at least one identifier
* gender 1..1 // Must have exactly one gender
* gender from http://hl7.org/fhir/ValueSet/administrative-gender (required)
Then PolarisCorePatient-example.json would look something like:
{
"resourceType": "Patient",
"meta": { // Indicates conformance
"profile": ["https://fhir.apps.health/StructureDefinition/polaris-core-patient"]
},
"identifier": [ // Array, because 1..*
{
"system": "https://fhir.apps.health/NamingSystem/cedarbrook-clinic-patient-identifier",
"value": "1234567890"
}
// ... potentially more identifiers
],
"gender": "female", // Single value, from the specified ValueSet
// ... other elements
}
Next Steps¶
- Explore
src/fsh/resources/: Pick a resource (e.g.,PolarisCoreObservation.fsh) and try to understand its rules. - Compare with Examples: Look at the corresponding file in
src/examples/(e.g.,PolarisCoreObservation-example.json) to see how the FSH translates. - FSH School: For a more in-depth tutorial on FSH, visit fshschool.org.
- Polaris FHIR Modelling Guidelines: Refer to the detailed guidelines provided to Fire Blackwell (your persona) for specific Polaris conventions and best practices.
This quick start should give you a foothold. The best way to learn FSH is by reading and writing it within the context of the Polaris project!