Measure/$evaluate-measure via SQL

FHIR Measure/$evaluate-measure implemented as native PostgreSQL SQL on Aidbox. No CQL engine required. 12 CMS quality measures, 484 test patients, interactive demo.

ComponentVersionLink
Aidboxedgedocs.aidbox.app
Python / Flask3.11 / 3.xflask.palletsprojects.com
Measure/$evaluate-measureFHIR R4hl7.org/fhir/R4 — $evaluate-measure
CQL SpecificationHL7build.fhir.org/ig/HL7/cql
CQL measuresdqm-content-qicore-2025github.com/cqframework/dqm-content-qicore-2025 — input/cql
Test datadqm-content-qicore-2025github.com/cqframework/dqm-content-qicore-2025 — input/tests

⚑ Solution

A working FHIR R4 Measure/$evaluate-measure implementation on Aidbox, without a CQL engine. Four things to know:

  1. 12 measures translated from CQL to native PostgreSQL SQL. Each measure is a single SQL file (sql/measures/cmsXXX/02-cmsXXX-measure.sql, ~200–400 lines) that replaces the original CQL definition. The translation was verified against the reference MeasureReports from dqm-content-qicore-2025 and independently with the cqframework CQL engine.
  2. Every supported measure is stored as a FHIR Measure resource in Aidbox (plus its Library resources), with canonical URL, version, groups and scoring metadata. Loaded via setup.py from data/measures-bundle.json. You can list them with GET /Measure and look one up by alias, FHIR Resource ID, or canonical URL.
  3. The operation is exposed on the Measure resource type — per the FHIR R4 spec the measure is passed as a parameter:
    POST /Measure/$evaluate-measure?measure=cms130&reportType=population&periodStart=2026-01-01&periodEnd=2026-12-31
    Instance-level invocation POST /Measure/{id}/$evaluate-measure is allowed by the spec but not implemented in this example.
  4. Response is a FHIR R4 MeasureReport with group.population counts (initial-population, denominator, denominator-exclusion, numerator), group.measureScore, and — for single-patient reports — evaluatedResource[] with references to the actual Encounter / Condition / Observation that triggered each population. Output aligns with the DEQM indv-measurereport-deqm profile.

Supported parameters: measure, subject, reportType (subject | subject-list | population), periodStart, periodEnd. Full details in API Parameters.

Jump to Quick Start to run it in five minutes, or Architecture to see how the request flows end to end.

01 Quick Start

Prerequisites: Docker with Docker Compose, Python 3, curl (optional, for manual API calls).

Build and start the full stack:

bash
docker compose up --build

This starts three containers: PostgreSQL, Aidbox (port 8888), and the SQL evaluate app (port 8090).

02 Activate Aidbox

Open http://localhost:8888 in your browser and follow the activation prompt.

One-time step. Aidbox requires browser activation on first launch. After activation, data persists across restarts.

03 Load Data

In a second terminal, from this directory:

bash
python3 setup.py

This creates shared views, loads terminology and clinical data for all 12 measures (~2 min).

Idempotent. Safe to run multiple times — views are replaced, data is upserted via PUT.

04 Evaluate a Measure

Open Aidbox REST Console and evaluate CMS130 for a single patient:

rest console
POST /Measure/$evaluate-measure?measure=cms130&subject=Patient/007ec5f1-08cf-474a-a472-f6a92cca4b79&periodStart=2026-01-01&periodEnd=2026-12-31

Summary report for CMS125 Breast Cancer Screening (all patients, read-only):

rest console
GET /Measure/$evaluate-measure?measure=cms125&reportType=population

Response — a FHIR MeasureReport with population counts and measure score:

response
{
  "resourceType": "MeasureReport",
  "status": "complete",
  "type": "individual",
  "measure": "http://ecqi.healthit.gov/ecqms/Measure/CMS130FHIRColorectalCancerScrn",
  "subject": {
    "reference": "Patient/007ec5f1-08cf-474a-a472-f6a92cca4b79"
  },
  "period": {
    "start": "2026-01-01",
    "end": "2026-12-31"
  },
  "group": [{
    "population": [
      {
        "code": { "coding": [{ "code": "initial-population" }] },
        "count": 1
      },
      {
        "code": { "coding": [{ "code": "denominator" }] },
        "count": 1
      },
      {
        "code": { "coding": [{ "code": "denominator-exclusion" }] },
        "count": 1
      },
      {
        "code": { "coding": [{ "code": "numerator" }] },
        "count": 0
      }
    ],
    "measureScore": {
      "value": null
    }
  }]
}
Pure SQL. No CQL engine involved — the measure logic runs as a single PostgreSQL query over Aidbox JSONB tables.

05 REST Console Examples

Open the Aidbox REST Console at http://localhost:8888/ui/console and paste any of the snippets below. The adapter resolves the measure parameter in three forms: short alias (cms130), FHIR Resource ID (CMS130FHIRColorectalCancerScrn), or canonical URL.

By alias — individual patient

CMS130 Colorectal Cancer Screening for a single patient:

rest console
POST /Measure/$evaluate-measure?measure=cms130&subject=Patient/007ec5f1-08cf-474a-a472-f6a92cca4b79

By canonical URL — summary (all patients)

CMS125 Breast Cancer Screening, population-level MeasureReport:

rest console
POST /Measure/$evaluate-measure?measure=https://madie.cms.gov/Measure/CMS125FHIRBreastCancerScreen

By alias with explicit period

CMS165 Controlling High Blood Pressure for a single patient, measurement period 2026:

rest console
POST /Measure/$evaluate-measure?measure=cms165&subject=Patient/045f7e0b-bfb3-4ee0-a06d-83c853f6a81e&periodStart=2026-01-01&periodEnd=2026-12-31

By FHIR Resource ID — individual patient

CMS131 Diabetes Eye Exam for a single patient:

rest console
POST /Measure/$evaluate-measure?measure=CMS131FHIRDiabetesEyeExam&subject=Patient/01a1241d-fd97-4c72-b288-fd31c4c7ae80
Tip. Omit subject for a summary report across all patients. Omit periodStart/periodEnd to use defaults (2026-01-01 to 2026-12-31).

06 Demo App

Start a local HTTP server from this directory:

bash
python3 -m http.server 3000

Then open the demo in your browser:

→ http://localhost:3000/demo/app.html

The demo shows all 12 measures with:

Note. The demo app uses the Aidbox $sql endpoint directly for fast batch evaluation across all patients.

Included Measures

12 measures 484 patients

CMS130

Colorectal Cancer Screening
64 patients

CMS125

Breast Cancer Screening
66 patients

CMS131

Diabetes Eye Exam
63 patients

CMS165

Controlling High Blood Pressure
68 patients

CMS124

Cervical Cancer Screening
34 patients

CMS139

Fall Risk Screening
29 patients

CMS75

Children Dental Decay
20 patients

CMS1154

Screening for Prediabetes
10 patients

CMS149

Dementia Cognitive Assessment
33 patients

CMS153

Chlamydia Screening
32 patients

CMS155

Weight Assessment / Counseling
34 patients

CMS143

POAG Optic Nerve Evaluation
32 patients

Aliases & Canonical URLs

Each measure can be referenced by short alias, FHIR Resource ID, or canonical URL in the measure parameter. Click any value to copy.

AliasFHIR Resource IDCanonical URL
cms130CMS130FHIRColorectalCancerScrnhttps://madie.cms.gov/Measure/CMS130FHIRColorectalCancerScrn
cms125CMS125FHIRBreastCancerScreenhttps://madie.cms.gov/Measure/CMS125FHIRBreastCancerScreen
cms131CMS131FHIRDiabetesEyeExamhttps://madie.cms.gov/Measure/CMS131FHIRDiabetesEyeExam
cms165CMS165FHIRControllingHighBPhttps://madie.cms.gov/Measure/CMS165FHIRControllingHighBP
cms124CMS124FHIRCervicalCancerScreenhttps://madie.cms.gov/Measure/CMS124FHIRCervicalCancerScreen
cms139CMS139FHIRFallRiskScreeninghttps://madie.cms.gov/Measure/CMS139FHIRFallRiskScreening
cms75CMS75FHIRChildrenDentalDecayhttps://madie.cms.gov/Measure/CMS75FHIRChildrenDentalDecay
cms1154CMS1154ScreeningPrediabetesFHIRhttps://madie.cms.gov/Measure/CMS1154ScreeningPrediabetesFHIR
cms149CMS149FHIRDementiaCognitiveAssesshttps://madie.cms.gov/Measure/CMS149FHIRDementiaCognitiveAssess
cms153CMS153FHIRChlamydiaScreeninghttps://madie.cms.gov/Measure/CMS153FHIRChlamydiaScreening
cms155CMS155FHIRWeightAssessmenthttps://madie.cms.gov/Measure/CMS155FHIRWeightAssessment
cms143CMS143FHIRPOAGOpticNerveEvalhttps://madie.cms.gov/Measure/CMS143FHIRPOAGOpticNerveEval

⚙ Architecture

End-to-end request flow for one Measure/$evaluate-measure call:

Client
Any FHIR consumer
POST /Measure/$evaluate-measure
?measure=cms130&reportType=population
↓
FHIR Server
Aidbox
routes via App resource — com.sql.evaluate.app
↓ HTTP-RPC
Custom Operation
sql-evaluate-app (Flask)
reads measure SQL → runs via $sql → builds MeasureReport
↓ POST /$sql
Storage
PostgreSQL (via Aidbox $sql)
FHIR resources stored as JSONB
↓
Response
FHIR MeasureReport
group.population (IP, Den, Exc, Num) + group.measureScore
+ evaluatedResource[] with Encounter/Condition refs

Inside the database, the SQL flows through four stable layers:

Aidbox JSONB → 8 flat views → concepts table → measure SQL CTEs → MeasureReport rows

Each measure is a single SQL file (~200–400 lines) that uses shared flat views and a concepts table for offline terminology matching. No CQL engine, no Java, no external terminology service calls at runtime.

How it works

  1. Client sends POST /Measure/$evaluate-measure?measure=cms130&subject=Patient/123
  2. Aidbox looks up App/com.sql.evaluate.app and forwards the request to the Flask app on port 8090
  3. Flask reads the measure SQL file (sql/measures/cmsXXX/02-cmsXXX-measure.sql), substitutes the measurement period, POSTs to Aidbox /$sql
  4. PostgreSQL runs the query: JSONB → flat views → concepts join → measure-specific CTEs (IP, exclusions, numerator)
  5. Flask aggregates the per-patient rows into populations and builds a FHIR R4 MeasureReport
  6. For single-patient reports, Flask also runs an evidence query and populates evaluatedResource[] with real resource references

⚙ API Parameters

ParameterStatusDescription
measurerequiredShort alias (cms130), FHIR Resource ID (CMS130FHIRColorectalCancerScrn), or canonical URL (https://madie.cms.gov/Measure/CMS130FHIRColorectalCancerScrn).
subjectsupportedPatient reference (e.g., Patient/123). Omit for summary report.
periodStartsupportedMeasurement period start. Default: 2026-01-01.
periodEndsupportedMeasurement period end. Default: 2026-12-31.
reportTypesupportedsubject (default when subject is provided), subject-list, or population (default when no subject).
practitionernot supportedPractitioner-scoped evaluation is out of scope for this example.
lastReceivedOnnot supportedIncremental reporting is out of scope for this example.

5 of 7 FHIR R4 $evaluate-measure parameters supported. See the R4 OperationDefinition for the full spec.

■ Shutdown

Stop all containers (data preserved in Docker volume):

bash
docker compose down

Or stop and delete all data (clean slate for next run):

bash
docker compose down -v