Clinical Reference Ranges and Interpretation Codes
By David Le -- Part 7 of the FhirHub Series
A blood pressure reading of 138/88 means nothing without context. Is that high? Critical? Normal for this patient? FhirHub implements clinical reference ranges based on published guidelines (AHA, ADA, KDIGO) and uses them to compute FHIR interpretation codes automatically.
Why Reference Ranges Matter
In clinical practice, every measurement has a normal range. Values outside that range trigger different levels of concern:
- Normal (N) -- Within expected bounds
- High (H) / Low (L) -- Outside normal, requires attention
- Critically High (HH) / Critically Low (LL) -- Immediate clinical action needed
FHIR represents these as interpretation codes on Observation resources. FhirHub computes them client-side using the clinical rules engine in frontend/src/lib/clinical-rules.ts.
Vital Signs Reference Ranges
FhirHub defines normal and critical ranges for each vital sign by LOINC code:
export const VITAL_REFERENCE_RANGES: Record<string, VitalRanges | null> = {
// Systolic Blood Pressure (mmHg) - AHA Guidelines
["8480-6"]: {
normal: { low: 90, high: 120 },
critical: { low: 70, high: 180 },
unit: "mmHg",
},
// Diastolic Blood Pressure (mmHg)
["8462-4"]: {
normal: { low: 60, high: 80 },
critical: { low: 40, high: 120 },
unit: "mmHg",
},
// Heart Rate (bpm)
["8867-4"]: {
normal: { low: 60, high: 100 },
critical: { low: 40, high: 150 },
unit: "bpm",
},
// Body Temperature (°F)
["8310-5"]: {
normal: { low: 97.8, high: 99.1 },
critical: { low: 95, high: 104 },
unit: "degF",
},
// Respiratory Rate (breaths/min)
["9279-1"]: {
normal: { low: 12, high: 20 },
critical: { low: 8, high: 30 },
unit: "/min",
},
// Oxygen Saturation (%)
["2708-6"]: {
normal: { low: 95, high: 100 },
critical: { low: 85 },
unit: "%",
},
// Body Weight - no standard range
["29463-7"]: null,
};
Note that body weight has no standard range (null) -- it varies too much by individual. The system handles this gracefully by skipping interpretation for vitals without defined ranges.
AHA Blood Pressure Categories
Blood pressure interpretation follows the American Heart Association (AHA) guidelines, which define six categories based on the combination of systolic and diastolic values:
export function computeBPInterpretation(
systolic: number,
diastolic: number
): { category: string; interpretation: InterpretationDTO } {
// Hypertensive Crisis (Critical high)
if (systolic >= 180 || diastolic >= 120) {
return {
category: "Hypertensive Crisis",
interpretation: createInterpretation("HH"),
};
}
// Severe Hypotension (Critical low)
if (systolic < 70 || diastolic < 40) {
return {
category: "Severe Hypotension",
interpretation: createInterpretation("LL"),
};
}
// Stage 2 Hypertension
if (systolic >= 140 || diastolic >= 90) {
return {
category: "Stage 2 Hypertension",
interpretation: createInterpretation("H"),
};
}
// Stage 1 Hypertension
if (systolic >= 130 || diastolic >= 80) {
return {
category: "Stage 1 Hypertension",
interpretation: createInterpretation("H"),
};
}
// Elevated
if (systolic >= 120 && diastolic < 80) {
return {
category: "Elevated",
interpretation: createInterpretation("H"),
};
}
// Hypotension
if (systolic < 90 || diastolic < 60) {
return {
category: "Hypotension",
interpretation: createInterpretation("L"),
};
}
// Normal
return {
category: "Normal",
interpretation: createInterpretation("N"),
};
}
The AHA categories mapped to FHIR interpretation codes:
| Category | Systolic | Diastolic | FHIR Code |
|---|---|---|---|
| Normal | < 120 | AND < 80 | N |
| Elevated | 120-129 | AND < 80 | H |
| Stage 1 Hypertension | 130-139 | OR 80-89 | H |
| Stage 2 Hypertension | >= 140 | OR >= 90 | H |
| Hypertensive Crisis | >= 180 | OR >= 120 | HH |
| Hypotension | < 90 | OR < 60 | L |
| Severe Hypotension | < 70 | OR < 40 | LL |
Lab Reference Ranges
FhirHub defines reference ranges for 21+ common lab tests:
export const LAB_REFERENCE_RANGES = {
glucose: { normal: { low: 70, high: 100 }, critical: { low: 50, high: 400 }, unit: "mg/dL" },
hba1c: { normal: { low: 4.0, high: 5.6 }, unit: "%" },
bun: { normal: { low: 7, high: 20 }, critical: { low: 2, high: 100 }, unit: "mg/dL" },
creatinine: { normal: { low: 0.7, high: 1.3 }, critical: { high: 10 }, unit: "mg/dL" },
egfr: { normal: { low: 60, high: 120 }, critical: { low: 15 }, unit: "mL/min/1.73m2" },
sodium: { normal: { low: 136, high: 145 }, critical: { low: 120, high: 160 }, unit: "mEq/L" },
potassium: { normal: { low: 3.5, high: 5.0 }, critical: { low: 2.5, high: 6.5 }, unit: "mEq/L" },
totalCholesterol: { normal: { high: 200 }, unit: "mg/dL" },
ldlCholesterol: { normal: { high: 100 }, unit: "mg/dL" },
hdlCholesterol: { normal: { low: 40 }, unit: "mg/dL" },
triglycerides: { normal: { high: 150 }, unit: "mg/dL" },
troponinI: { normal: { high: 0.04 }, unit: "ng/mL" },
bnp: { normal: { high: 100 }, unit: "pg/mL" },
inr: { normal: { low: 2.0, high: 3.0 }, unit: "" },
inrTherapeutic: { normal: { low: 2.0, high: 3.0 }, critical: { low: 1.5, high: 4.0 }, unit: "" },
inrBaseline: { normal: { low: 0.9, high: 1.1 }, unit: "" },
pt: { normal: { low: 11, high: 13.5 }, unit: "s" },
};
Key details:
- Not all ranges are symmetric. HDL cholesterol only has a
lowthreshold (higher is better). Troponin I only has ahighthreshold (any elevation is concerning). - Some tests have multiple range profiles. INR has three: therapeutic range (for patients on anticoagulation), baseline (for patients not on anticoagulation), and a general range.
- Critical ranges are optional. HbA1c doesn't define critical thresholds -- any abnormal value warrants attention but isn't immediately dangerous.
Computing Interpretations
The interpretation engine converts a numeric value into a FHIR interpretation code:
export function computeVitalInterpretation(
loincCode: string,
value: number
): InterpretationDTO {
const referenceRange = getVitalReferenceRange(loincCode);
const criticalRange = getVitalCriticalRange(loincCode);
return computeInterpretation(value, referenceRange, criticalRange);
}
The computeInterpretation utility checks the value against both the normal and critical ranges, returning the appropriate FHIR interpretation code (N, H, L, HH, LL).
FHIR ReferenceRange DTOs
Reference ranges are converted to FHIR-compliant DTOs for API communication:
export function getVitalReferenceRange(
loincCode: string
): ReferenceRangeDTO | undefined {
const ranges = VITAL_REFERENCE_RANGES[loincCode];
if (!ranges) return undefined;
return {
low: ranges.normal.low !== undefined
? { value: ranges.normal.low, unit: ranges.unit, system: UCUM_SYSTEM }
: undefined,
high: ranges.normal.high !== undefined
? { value: ranges.normal.high, unit: ranges.unit, system: UCUM_SYSTEM }
: undefined,
type: "normal",
};
}
Units use the UCUM (Unified Code for Units of Measure) system, which is the standard coding system for units in FHIR.
Clinical Alerts
The reference ranges power FhirHub's clinical alerts system. When a vital sign or lab result falls outside normal ranges, the system generates an alert visible on the dashboard. Critical values (HH/LL) are surfaced with higher priority.
What's Next
In Part 8, we'll examine the bulk data export feature -- from the 4-step wizard UI to the job lifecycle management in the API.