Introduction to CQL on FHIR
Clinical Quality Language (CQL) is a high-level, domain-specific language focused on clinical quality and is designed to be both human-readable and machine-processable. When combined with FHIR, CQL enables the expression of clinical knowledge in a way that can be executed against FHIR data sources.
Why CQL + FHIR?
- Standardization: CQL provides a standard way to express clinical logic
- Interoperability: Works seamlessly with FHIR data models
- Quality Measures: Essential for eCQMs (electronic Clinical Quality Measures)
- Clinical Decision Support: Enables sophisticated CDS rules
Key Components
CQL Libraries
Reusable collections of CQL expressions and functions
FHIR Resources
Patient data represented as FHIR resources (Patient, Observation, etc.)
Measure Resources
FHIR Measure resources that define quality measures
Example: Breast Cancer Screening Quality Measure
Let's explore a real-world example: measuring breast cancer screening rates. This is based on the CMS 125 measure - women ages 50-74 who had a mammogram within the past 27 months.
1. Measure Definition
Clinical Criteria:
- Population: Women ages 50-74
- Denominator: All women in the age range
- Numerator: Women who had a mammogram in the past 27 months
- Exclusions: Women with bilateral mastectomy, certain diagnoses
2. CQL Library Structure
library BreastCancerScreening version '1.0.0'
using FHIR version '4.0.1'
include FHIRHelpers version '4.0.1'
include MATGlobalCommonFunctions version '5.0.000' called Global
codesystem "LOINC": 'http://loinc.org'
codesystem "SNOMEDCT": 'http://snomed.info/sct'
valueset "Mammography": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1018'
valueset "Bilateral Mastectomy": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1005'
parameter "Measurement Period" Interval<DateTime>
context Patient
// Initial Population
define "Initial Population":
Patient.gender = 'female'
and AgeInYearsAt(start of "Measurement Period") >= 50
and AgeInYearsAt(start of "Measurement Period") < 75
// Denominator
define "Denominator":
"Initial Population"
// Numerator
define "Numerator":
exists (
[Observation: "Mammography"] Mammogram
where Mammogram.status in { 'final', 'amended', 'corrected' }
and Mammogram.effective during Interval[
start of "Measurement Period" - 27 months,
end of "Measurement Period"
]
)
// Denominator Exclusions
define "Denominator Exclusions":
exists (
[Procedure: "Bilateral Mastectomy"] BilateralMastectomy
where BilateralMastectomy.status = 'completed'
and BilateralMastectomy.performed before end of "Measurement Period"
)3. FHIR Measure Resource
{
"resourceType": "Measure",
"id": "breast-cancer-screening",
"url": "http://example.org/Measure/breast-cancer-screening",
"version": "1.0.0",
"name": "BreastCancerScreening",
"title": "Breast Cancer Screening",
"status": "active",
"experimental": false,
"date": "2025-01-01",
"description": "Percentage of women 50-74 years of age who had a mammogram to screen for breast cancer",
"purpose": "To measure the rate of breast cancer screening in eligible women",
"scoring": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/measure-scoring",
"code": "proportion",
"display": "Proportion"
}]
},
"type": [{
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/measure-type",
"code": "process",
"display": "Process"
}]
}],
"improvementNotation": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/measure-improvement-notation",
"code": "increase",
"display": "Increased score indicates improvement"
}]
},
"library": [
"http://example.org/Library/BreastCancerScreening"
],
"group": [{
"population": [{
"code": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/measure-population",
"code": "initial-population"
}]
},
"criteria": {
"language": "text/cql",
"expression": "Initial Population"
}
}, {
"code": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/measure-population",
"code": "denominator"
}]
},
"criteria": {
"language": "text/cql",
"expression": "Denominator"
}
}, {
"code": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/measure-population",
"code": "numerator"
}]
},
"criteria": {
"language": "text/cql",
"expression": "Numerator"
}
}, {
"code": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/measure-population",
"code": "denominator-exclusion"
}]
},
"criteria": {
"language": "text/cql",
"expression": "Denominator Exclusions"
}
}]
}]
}4. Sample FHIR Data
Example patient data that would be evaluated by this measure:
// Patient Resource
{
"resourceType": "Patient",
"id": "patient-example",
"gender": "female",
"birthDate": "1968-03-15"
}
// Observation Resource (Mammogram)
{
"resourceType": "Observation",
"id": "mammogram-example",
"status": "final",
"code": {
"coding": [{
"system": "http://loinc.org",
"code": "24604-1",
"display": "Mammography"
}]
},
"subject": {
"reference": "Patient/patient-example"
},
"effectiveDateTime": "2024-06-15",
"valueString": "Screening mammogram completed - no abnormalities detected"
}Implementation Steps
Step 1: Set Up CQL Execution Engine
Choose and configure a CQL execution engine. Popular options include:
- cql-execution: JavaScript-based CQL engine by MITRE
- CQL Evaluator: Java-based engine integrated with HAPI FHIR
- Clinical Quality Framework (CQF): Reference implementation
npm install cql-execution cql-exec-fhirStep 2: Translate CQL to ELM
CQL must be translated to Expression Logical Model (ELM) JSON for execution:
npm install -g cql-to-elm
cql-to-elm --input BreastCancerScreening.cql --output BreastCancerScreening.jsonStep 3: Execute Measure Evaluation
Use the $evaluate-measure operation on a FHIR server:
POST [base]/Measure/breast-cancer-screening/$evaluate-measure
Content-Type: application/fhir+json
{
"resourceType": "Parameters",
"parameter": [{
"name": "periodStart",
"valueDate": "2024-01-01"
}, {
"name": "periodEnd",
"valueDate": "2024-12-31"
}, {
"name": "subject",
"valueString": "Patient/patient-example"
}, {
"name": "reportType",
"valueCode": "individual"
}]
}Step 4: Process MeasureReport
The server returns a MeasureReport resource with evaluation results:
{
"resourceType": "MeasureReport",
"status": "complete",
"type": "individual",
"measure": "http://example.org/Measure/breast-cancer-screening",
"subject": {
"reference": "Patient/patient-example"
},
"period": {
"start": "2024-01-01",
"end": "2024-12-31"
},
"group": [{
"population": [{
"code": {
"coding": [{
"code": "initial-population"
}]
},
"count": 1
}, {
"code": {
"coding": [{
"code": "denominator"
}]
},
"count": 1
}, {
"code": {
"coding": [{
"code": "numerator"
}]
},
"count": 1
}]
}],
"evaluatedResource": [{
"reference": "Patient/patient-example"
}, {
"reference": "Observation/mammogram-example"
}]
}Best Practices
✓ Do's
- ✅ Use standard value sets from VSAC (Value Set Authority Center)
- ✅ Include version numbers in library declarations
- ✅ Write comprehensive unit tests for CQL logic
- ✅ Document clinical rationale for each criterion
- ✅ Use FHIRPath expressions when possible
- ✅ Leverage existing CQL libraries (MAT Global Common Functions)
✗ Don'ts
- ❌ Hardcode codes instead of using value sets
- ❌ Ignore data quality and completeness issues
- ❌ Create overly complex nested logic
- ❌ Skip validation against test patient data
- ❌ Forget to handle missing or null data
- ❌ Neglect performance optimization for large datasets
Testing & Validation
CQL Test Framework
The CQL Framework provides comprehensive testing capabilities:
Latest Testing Resources:
- CQL Tests Results Repository →
Comprehensive test suite with expected results for CQL implementations
- CQ Framework GitHub Organization →
Official CQL specifications, tools, and reference implementations
Example Test Case
{
"name": "Patient Meets Numerator - Recent Mammogram",
"description": "55-year-old female with mammogram 6 months ago should be in numerator",
"patientData": {
"resourceType": "Patient",
"id": "test-patient-1",
"gender": "female",
"birthDate": "1969-01-01"
},
"observations": [{
"resourceType": "Observation",
"status": "final",
"code": {
"coding": [{
"system": "http://loinc.org",
"code": "24604-1"
}]
},
"effectiveDateTime": "2024-06-01"
}],
"measurementPeriod": {
"start": "2024-01-01",
"end": "2024-12-31"
},
"expectedResults": {
"initialPopulation": true,
"denominator": true,
"numerator": true,
"denominatorExclusion": false
}
}Common CQL Patterns
Date Range Queries
define "Observations in Measurement Period":
[Observation] O
where O.effective during "Measurement Period"
and O.status in { 'final', 'amended', 'corrected' }Value Set Membership
define "Diabetes Diagnoses":
[Condition: "Diabetes"] DiabetesCondition
where DiabetesCondition.clinicalStatus ~ "active"
and DiabetesCondition.verificationStatus ~ "confirmed"Age Calculations
define "Patient Age at Start":
AgeInYearsAt(start of "Measurement Period")
define "Eligible Age Range":
"Patient Age at Start" >= 50 and "Patient Age at Start" < 75Exists vs Count
// Use exists for boolean checks (more efficient)
define "Has Recent Lab":
exists ([Observation: "Lab Test"] O where O.effective during "Recent Period")
// Use count when you need the actual number
define "Number of Encounters":
Count([Encounter] E where E.period during "Measurement Period")Additional Resources
Need Help Implementing CQL Quality Measures?
Our experts can help you design, implement, and validate CQL-based quality measures for your organization.