FHIRPath in the builder

FHIRPath is the expression language the builder uses for everything dynamic: computed answers, initial values, conditional visibility, dynamic option lists and validation. Expressions run in the browser through the bundled FHIRPath engine, evaluated live against the current form state.

What expressions run against

Every expression is evaluated against the questionnaire's live QuestionnaireResponse. That resource is exposed as %resource, so you read answers with normal FHIRPath, for example:

%resource.item.where(linkId='weight').answer.value

Alongside %resource, expressions can use:

  • %varName — any questionnaire-level variable you have defined (see below).

The builder's own engine does not provide %questionnaire or launch-context variables such as %patient, %user or %encounter. You can still declare launch contexts so that an SDC server can use them, and the patient presets in the top bar set the %age, %gender and similar variables for testing.

Questionnaire variables

The Variables card above the builder tree defines reusable, questionnaire-level FHIRPath variables (the core variable extension). Each variable has a name and an expression and is referenced elsewhere as %name.

Variables are evaluated in order, and a later variable may reference an earlier one. They are imported and exported as variable extensions (files that use the older sdc-questionnaire-variable address are still read). The card's ↺ Re-init button re-evaluates all variables and item initialExpression fields.

Launch contexts

Launch contexts declare named resources a questionnaire expects at run time — %patient, %user, %encounter and so on (the SDC sdc-questionnaire-launchContext extension). They are configured in the Questionnaire properties and are what an SDC server uses to pre-populate the form via $populate.

Where FHIRPath is used

The same engine powers several features, each backed by a different FHIR field:

Feature FHIR field Purpose
Calculated answer calculatedExpression (SDC) Compute a read-only value from other answers
Initial value initialExpression (SDC) Seed an answer when the form loads
Visibility enableWhenExpression (SDC) Show/hide an item — see enableWhen & visibility
Dynamic options answerExpression (SDC) Produce choice options at render time
Validation questionnaire-constraint expression Must return true to pass — uses %resource (%questionnaire is not available in the builder's own engine)

Build expressions visually

You don't have to write FHIRPath by hand. Wherever an expression field appears, a 🧩 Build… button opens a small block builder that assembles the correct FHIRPath for you and shows a live result as you go — pick questions, variables and values instead of typing paths and accessors:

  • Conditions (enableWhenExpression) — shown as a tree of nested ALL (AND) / ANY (OR) groups; each leaf is one rule (question, comparison or "has answer"), with a live ✓/✗ on every node. Any expression, however nested, stays visible — unrecognised parts are editable as text only at the leaf.
  • Values (calculatedExpression, initialExpression) — an arithmetic chain of questions, variables and numbers joined by × ÷ + −, plus aggregates (count, sum, average, min, max) over a repeating question's answers.
  • Answers → value (calculatedExpression) — a collection pipeline over a question's answers: pick a source question, filter the answers — keep or drop values against a set (intersect / exclude), take unique values, or keep where the value compares to a number/string (> ≥ < ≤ = ≠) — then reduce to a value: join into text, count, "any exists", first or last. This builds expressions such as …answer.valueCoding.code.intersect(('43633'|'43644')).join(', ') or …answer.valueInteger.where($this > 5).count() without typing.

On an empty value field the builder first asks what the expression should produce (Number, Yes / No condition, or Answers → value). Press Apply to drop the result into the field. Opening the builder on an existing expression reads it back into blocks where possible; anything it can't represent stays editable as text.

  • Number — an arithmetic chain: questions, variables and numbers joined by × ÷ + −, plus aggregate functions (count, sum, avg, min, max) over repeating answers. Use this for scoring, BMI formulas, or any derived number.
  • Yes / No condition — a boolean condition tree (the same editor used for enableWhenExpression): nested AND / ANY groups with leaf comparisons and "has answer" checks. Use this when the calculated result is true/false — for example, to compute a boolean flag from several criteria that drives a downstream enableWhen.
  • Answers → value — see the pipeline description above.

On a value field, the type chooser's 📐 Choose from gallery option opens a searchable library of ready-made calculations (BMI, eGFR, PHQ-9, due date and many more) instead of building from scratch. The result is ordinary, editable FHIRPath. See Calculation gallery for how it works and what it offers.

Understanding a result (Expression Explain)

Next to a calculated value or a visibility condition, a small ✓/✗ icon appears once the expression has a result. Clicking it opens Expression Explain — a breakdown of the expression as a tree (for conditions: each AND/OR branch and leaf comparison with its own ✓/✗) so you can see exactly which part of a condition passed or failed, not just the overall outcome.

Evaluation order

Calculated fields often depend on one another (A → B → C). The runtime builds a dependency graph from the expressions and evaluates calculated nodes in topological order, so a chain resolves correctly no matter how the items are arranged in the tree. Each computed value is written back into the QuestionnaireResponse within the same pass, so downstream expressions read the fresh value. Circular dependencies are evaluated best-effort in tree order.

Live and safe to experiment

Expressions are re-evaluated on every edit, and the preview updates immediately. An expression that fails to parse or throws is skipped rather than breaking the form, so you can iterate on a formula and watch the result change as you type. A short example of the kind of expression you might write:

%age > 18 and %gender = 'male'

Testing expressions (FHIRPath tester)

To try an expression without wiring it into an item, open ⚙ Settings ▾ → 🧪 FHIRPath tester…. A small console opens above the preview and evaluates whatever you type against the current QuestionnaireResponse — the same runtime the preview uses — with %resource and all questionnaire %variables in scope, showing the result (or a parse error) live. Because it sits above the form, you can keep editing answers with it open and watch the value change, e.g. %resource.descendants().where(linkId='bmi').answer.valueDecimal.


Next: Extensions & SDC.