Developer ← Insights

FHIR Profiles: Design and Implementation with FSH, SUSHI, and the Validator CLI

A FHIR profile is a StructureDefinition resource that constrains or extends a base FHIR resource to meet the requirements of a specific use case, jurisdiction, or implementation guide. Profiles declare which elements are mandatory, which are prohibited, what cardinalities apply, which code systems must bind to coded elements, and what extensions are needed beyond the base specification. In our experience, a well-designed profile is the difference between an integration that interoperates at a purely technical level and one that genuinely exchanges meaningful clinical data with correct semantics — the base Patient or Observation resource is intentionally loose, and it is the profile that turns "valid FHIR" into "valid for your exchange."

Designing the Profile: Constraints, Cardinalities, and Must-Support

Design starts by identifying the base resource (Patient, Observation, MedicationRequest) and the constraints your context actually requires. Three decisions dominate:

  • Resource-level profiling vs. logical models first: Profiling directly on resources is faster; defining logical models first keeps your clinical requirements independent of FHIR mechanics and pays off when the same model must target multiple FHIR versions or standards. In our experience, teams with existing clinical information models (including openEHR archetypes) benefit from modeling first and mapping second.
  • Cardinality aggressiveness: Every element you force to 1..1 excludes every real-world system that cannot reliably populate it. Constrain what the use case demands; leave implementer flexibility elsewhere. Remember that in FHIR, minimum cardinality 1 alone doesn't guarantee useful data is present — additional FHIRPath invariants are often needed.
  • Must-support vs. obligations: In FHIR R4, flagging an element mustSupport means implementations SHALL support it "in some meaningful way" — but the flag itself does not define what "support" means, so the profile or IG must spell it out in narrative [3]. FHIR R5 addressed this ambiguity by introducing obligations: machine-readable codes attached via the obligation extension that state precisely what an actor must do (populate, display, store, process), and the extension is defined so it can also be used with earlier FHIR versions [4]. If you are authoring R4 profiles today, be explicit in narrative about your must-support semantics — vague must-support flags are one of the most common sources of certification disputes.

Authoring with FHIR Shorthand and SUSHI

FHIR Shorthand (FSH) has become the standard authoring approach: a domain-specific language, published as an HL7 standard (Mixed Normative/Trial Use since February 2022), that expresses profiles, extensions, value sets, and code systems in concise text that lives happily in Git [1]. SUSHI, the reference compiler, translates FSH into the JSON StructureDefinitions consumed by validators, servers, and the IG Publisher, and supports FHIR R4, R4B, and R5 [2]. A minimal Patient profile looks like this:

// patient-profile.fsh
Alias: $mrn-system = https://example.org/fhir/NamingSystem/mrn

Profile: AcmePatient
Parent: Patient
Id: acme-patient
Title: "ACME Patient Profile"
Description: "Patient with a mandatory MRN identifier and known name."
* identifier 1..* MS
* identifier ^slicing.discriminator.type = #value
* identifier ^slicing.discriminator.path = "system"
* identifier ^slicing.rules = #open
* identifier contains mrn 1..1 MS
* identifier[mrn].system = $mrn-system
* identifier[mrn].value 1..1
* name 1..* MS
* gender 1..1
* birthDate MS

Compile it with SUSHI (installed via npm install -g fsh-sushi):

sushi .   # emits fsh-generated/resources/StructureDefinition-acme-patient.json

By default SUSHI generates only the profile differential — the set of changes your profile makes relative to its parent — and leaves snapshot generation to the IG Publisher, the approach recommended by HL7 FHIR leadership. The snapshot is the fully resolved element list: the differential merged onto every inherited element from the base resource, so it stands alone without needing the parent to be interpreted. The distinction matters at deployment time, because most FHIR servers and validators consume the snapshot, not the differential — a StructureDefinition shipped with only a differential will typically fail on import into a server or terminology tool. If you need SUSHI to produce the snapshot directly, without running the IG Publisher, use the --snapshot (-s) flag:

sushi build . --snapshot   # emits StructureDefinitions containing a resolved snapshot

The equivalent can be set persistently via the snapshot property in sushi-config.yaml. In practice, let the IG Publisher own snapshot generation for anything you publish as an IG, and reach for --snapshot when you are loading standalone profiles straight into a server or CI validation step that expects resolved definitions.

Validating Instances Against the Profile

Implementation means proving that every resource your system produces conforms. The official FHIR Validator CLI (validator_cli.jar, released from the org.hl7.fhir.core project) validates instances against a profile and reports violations with path-level detail — element path, line, and column [5]:

java -jar validator_cli.jar sample-patients/*.json \
  -version 4.0 \
  -ig fsh-generated/resources \
  -profile https://example.org/fhir/StructureDefinition/acme-patient

Beyond the CLI, test platforms such as Inferno and Touchstone exercise conformance at the API level, which matters when your profiles are part of a certification program rather than an internal contract.

A common pitfall is publishing profiles that are theoretically correct but computationally expensive to validate at runtime. Deep semantic validation — terminology expansion, slicing evaluation, invariant execution — has real cost; even the HAPI FHIR documentation explicitly warns that the performance implications of running its instance validator in a production system are always worth considering. In our experience, heavy slicing with complex discriminators and large value set expansions are the usual culprits, and profiling your validator with realistic message volumes belongs on the pre-production checklist, not the incident postmortem.

Profile Design Decisions at a Glance

Decision Options Key Trade-off
Modeling approach Profile resources directly vs. logical model first Speed vs. independence from FHIR version mechanics and reuse across standards
Cardinality Strict (1..1) vs. permissive (0..1 + must-support) Data guarantees vs. excluding systems that can't populate the element
Conformance expectations R4 mustSupport + narrative vs. R5 obligation codes Broad tooling support today vs. machine-readable, actor-specific precision
Terminology bindings required / extensible / preferred / example Semantic interoperability vs. validation cost and implementer flexibility
Validation strategy CLI in CI pipeline, server-side on ingest, test platforms Early feedback vs. runtime performance under production volumes

Where CaboLabs Fits

Profiles are a governance asset, not a one-off deliverable: they evolve with regulation, terminology releases, and new trading partners, and they need versioning, review, and regression validation like any other code. CaboLabs works at exactly this intersection of clinical modeling and engineering: we design and implement FHIR profiles and implementation guides, build FSH/SUSHI authoring pipelines with CI validation, and integrate FHIR interfaces with standards-based clinical persistence — including Atomik, our openEHR-native clinical data repository, for architectures where FHIR handles the exchange layer and openEHR provides the queryable, vendor-neutral storage layer beneath it.

If your team is authoring its first profiles, untangling must-support semantics for a certification, or connecting FHIR APIs to a clinical data platform, reach out at cabolabs.com — we'll help you design profiles that validate cleanly, perform in production, and survive their next version.

References & Verifiable Sources

  1. HL7 International: FHIR Shorthand (FSH) Specification (Official HL7 standard for the FSH language — Mixed Normative/Trial Use since February 2022 — and documentation of SUSHI as the reference compiler; supports the authoring section).
  2. FHIR / SUSHI project: SUSHI — FSH Compiler (GitHub) (Reference implementation compiling FSH into FHIR artifacts; confirms support for creating implementation guides targeting FHIR R4, R4B, and R5).
  3. HL7 International: FHIR R4 — Conformance Rules (Official definition of cardinality and the mustSupport flag, including that labeling an element MustSupport means implementations SHALL support it "in some meaningful way" without defining what support means; supports the must-support discussion).
  4. HL7 International: FHIR R5 — Obligations (Official R5 page introducing machine-readable obligations attached to must-support elements via the obligation extension; supports the R4 vs. R5 distinction).
  5. HL7 FHIR Confluence: Using the FHIR Validator (Official documentation for validator_cli.jar, including download location, -version, -ig, and -profile parameters; supports the validation section and command examples).

Do you have any questions?

Let us know how we can help you.

Company CaboLabs Health Informatics
Address Juan Paullier 995, Montevideo, Uruguay
Phone +598 99 043 145