Skip to content

FSH 101: Quick Start Guide for Nexus EMR Developers

Welcome, Nexus EMR developer! You're about to dive into FHIR Shorthand (FSH), the language we use to define our Nexus EMR 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 Nexus EMR 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 (StructureDefinition resources).
  • SUSHI: The command-line tool (compiler) that takes your FSH files (.fsh) as input and generates the official FHIR JSON StructureDefinition files. These JSON files are then used by the IG Publisher to build our Implementation Guide.
  • Nexus EMR Standard: All Nexus EMR FHIR specifications are authored in FSH.

Core FSH Syntax Elements (with Nexus EMR Examples)

FSH files are plain text files, typically with a .fsh extension. Let's look at the common building blocks you'll see in Nexus EMR 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.
*/
Nexus EMR convention: The first line of every FSH file is // Nexus EMR<ResourceType>.fsh Example: // NexusEmrCorePatient.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>
  • Nexus EMR 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 NexusEmrCorePatient profile's canonical URL
Alias: $NexusEmrCorePatient = https://fhir.apps.health/StructureDefinition/nexus-emr-core-patient
Once defined, you use the alias (e.g., $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...
    
  • Nexus EMR Example (NexusEmrCorePatient.fsh excerpt):
    Profile:        NexusEmrCorePatient
    Parent:         Patient // Based on the core Patient resource
    Id:             nexus-emr-core-patient // kebab-case, used in URLs
    Title:          "Nexus EMR Patient"
    Description:    "Generic Nexus EMR core profile for patient demographic and administrative information..."
    * ^url = $NexusEmrCorePatient // Sets the canonical URL using an alias
    * ^status = #draft       // All Nexus EMR profiles start as draft
    
    The * ^url = ... and * ^status = ... are "caret path" rules, setting metadata on the StructureDefinition itself.

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)
  • Nexus EMR 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> ...
  • Nexus EMR Example (NexusEmrCoreObservation.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. Nexus EMR typically uses required or extensible.
  • Nexus EMR Example (NexusEmrCorePatient.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>"
  • Nexus EMR Example (NexusEmrCoreMedicationRequest.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>)
  • Nexus EMR Example (NexusEmrCoreCondition.fsh):
    // The subject of a NexusEmrCoreCondition must be a NexusEmrCorePatient
    * subject only Reference(NexusEmrCorePatient)
    

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 obeys rule: * obeys <invariant-id>
  • Nexus EMR Example (common pattern):
    Invariant: patient-name-family-or-given
    Description: "Patient.name: at least one name SHALL carry a family name, a given name, or text -- or a data-absent-reason extension (CA Baseline ipa-pat-2). Ontario's stricter 'family required' is applied as an edge transform on submission, not in the core profile."
    Severity: #warning
    Expression: "name.where(family.exists() or given.exists() or text.exists()).exists() or name.extension.where(url = 'http://hl7.org/fhir/StructureDefinition/data-absent-reason').exists()"
    
    // In StructureDefinition-nexus-emr-core-patient.fsh:
    Profile: NexusEmrCorePatient
    // ...
    
    * obeys patient-name-family-or-given // 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
    
  • Nexus EMR Example (NexusEmrCoreCondition.fsh):
    Alias: $VS_NexusEmrConditionCodes = https://fhir.apps.health/ValueSet/nexus-emr-core-condition-codes
    
    ValueSet: NexusEmrConditionCodesVS
    Title: "Nexus EMR Condition Codes"
    Description: "ValueSet for condition codes used in Nexus EMR."
    
    * ^url = $VS_NexusEmrConditionCodes
    * ^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)
    
  • Nexus EMR Example (NexusEmrCoreAppState.fsh):
    Extension: AppStateStringValue
    Id:        app-state-string-value
    Title:     "App State String Value Extension"
    Description: "A simple extension to hold the value of a Nexus EMR 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 NexusEmrCorePatient.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 NexusEmrCorePatient-example.json would look something like:

{
  "resourceType": "Patient",
  "meta": { // Indicates conformance
    "profile": ["https://fhir.apps.health/StructureDefinition/nexus-emr-core-patient"]
  },
  "identifier": [ // Array, because 0..*
    {
      "system": "https://fhir.infoway-inforoute.ca/NamingSystem/ca-ab-patient-healthcare-id",
      "value": "123456789"
    }
    // ... potentially more identifiers
  ],
  "gender": "female", // Single value, from the specified ValueSet
  // ... other elements
}


Next Steps

  1. Explore src/fsh/resources/: Pick a resource (e.g., NexusEmrCoreObservation.fsh) and try to understand its rules.
  2. Compare with Examples: Look at the corresponding file in src/examples/ (e.g., NexusEmrCoreObservation-example.json) to see how the FSH translates.
  3. FSH School: For a more in-depth tutorial on FSH, visit fshschool.org.
  4. Nexus EMR FHIR Modelling Guidelines: Refer to the detailed guidelines provided to Fire Blackwell (your persona) for specific Nexus EMR 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 Nexus EMR project!