National Eye Institute, Office of Data Science and Health Informatics
May 30, 2024
FHIR® is the registered trademark of Health Level Seven International (HL7). Use of the FHIR trademark does not constitute an HL7 endorsement of this workshop.
© 2024 The MITRE Corporation / Approved for Public Release / Case #24-0169
Today’s use case: creating a novel clinical decision support (CDS) tool for ophthalmology that integrates into an electronic health record (EHR) workflow
We will identify and work hands-on with the parts of the FHIR ecosystem that support this use case
Workshop content is organized from least-to-most technical
No prior FHIR experience or other technical skills* are needed
*Except for at the very end of the workshop: we will be working hands-on with Python
neiodshi@nei.nih.gov
Part 1: Overview of…
Part 2: Deep dive into…
Part 3: Hands-on with…
FHIR stands for:
FHIR® is a standard for exchanging health information electronically.
Standards establish a common language and process for all health information technology (IT) systems to communicate, allowing information to be shared seamlessly and efficiently.
The FHIR specification: https://hl7.org/fhir/
Standard data elements grouped into “resources”
HL7 and the FHIR Community (e.g., chat.fhir.org)
Customization via FHIR implementation guides (e.g., US Core; others)
Diagram of typical FHIR system architecture.
Provides a uniform interface between FHIR-enabled systems that allows them to talk to each other
Practically, allows you to construct a URL like http://hapi.fhir.org/baseR4/Patient/44728118
to retrieve specific information
Base: http://hapi.fhir.org/baseR4
Resource: Patient
Id: 44728118
More complex retrieval is possible via the FHIR API:
Get a patient with a specific MRN: http://hapi.fhir.org/baseR4/Patient/?identifier=68519946
Get all patients who have a specific kind of observation: http://hapi.fhir.org/baseR4/Patient/?_has:Observation:subject:code=41633001
Sending data, updating data, etc. have similar standardized interfaces
The goal of the original SMART on FHIR API is audacious and can be expressed concisely: an innovative app developer can write an app once and expect that it will run anywhere in the health care system.
…
SMART provides a full stack of open specifications that enable a medical apps platform.
SMART stands for “Substitutable Medical Apps, Reusable Technology”, a standard by the SMART Health IT group
Based on open standards: FHIR, OAuth2, OpenID Connect
Widely implemented/used (e.g., used on all iPhones for connecting to patient portals)
Required as part of ONC certification for 21st Century Cures Act: §170.315(g)(10) Standardized API for patient and population services
SMART on FHIR lets you:
It ties together existing common web standards and HL7 specifications to enable secure EHR integration:
patient-view
hook is triggered when the patient record is opened, which could then call natural language processing softwareDiagram of example CDS system architecture used for this workshop.
The demo is using this Docker Compose setup:
https://github.com/NIH-NEI/fhir-for-research-smart-example
compose.yaml
defines the 5 components from the previous slideI have this running on localhost
:
HAPI server: http://localhost:8080
Launch simulator: http://localhost:8010
CDS back-end server (Python): http://localhost:8081
Example PACS sever: http://localhost:8082
User interface (JavaScript): http://localhost:3000
If you are not coming back after the break:
Please fill out the survey:
Save these links:
Workshop materials: https://github.com/NIH-NEI/fhir-for-research-workshop
More on FHIR for Research: http://purl.org/fhir-for-research/web
Contact us: neiodshi@nei.nih.gov
Diagram of example CDS system architecture used for this workshop.
Our version of this defined by compose.yaml
, and that GitHub repository also contains code for the example front-end UI, CDS back-end server, and PACS mock-up.
Sequence diagram of SMART on FHIR log-in/permissions workflow as implemented in the example for this workshop.
Available on GitHub: https://github.com/NIH-NEI/fhir-for-research-smart-example/tree/main/example_ui
SMART on FHIR walkthrough
Your browser (a client) gets redirected by the EHR to your app’s http://localhost:3000/launch.html with the following parameters:
iss
: Identifies the EHR’s endpoint for the applaunch
: An opaque identifier for this specific app launch and EHR context, required for security purposes
SMART on FHIR walkthrough
launch.html
executes an authorization request with select parametersSMART on FHIR walkthrough
launch.html
executes an authorization request with select parameters<script>
FHIR.oauth2.authorize({
// The client_id that you should have obtained after registering a client at
// the EHR.
//
// Note that this can be an arbitrary string when testing with
// http://launch.smarthealthit.org.
clientId: "my_web_app",
// The scopes that you request from the EHR. In this case we want to:
// launch - Get the launch context
// openid & fhirUser - Get the current user
// patient/*.read - Read patient data
scope: "launch openid fhirUser patient/*.read",
// Typically, if your redirectUri points to the root of the current directory
// (where the launchUri is), you can omit this option because the default value is
// ".". However, some servers do not support directory indexes so "." and "./"
// will not automatically map to the "index.html" file in that directory.
redirectUri: "index.html"
});
</script>
SMART on FHIR walkthrough
The clientId
parameter is a specific string obtained after registering the app in the EHR manually. You would replace "my_web_app"
with a specific app identifier for a production implementation.
<script>
FHIR.oauth2.authorize({
// The client_id that you should have obtained after registering a client at
// the EHR.
//
// Note that this can be an arbitrary string when testing with
// http://launch.smarthealthit.org.
clientId: "my_web_app",
// The scopes that you request from the EHR. In this case we want to:
// launch - Get the launch context
// openid & fhirUser - Get the current user
// patient/*.read - Read patient data
scope: "launch openid fhirUser patient/*.read",
// Typically, if your redirectUri points to the root of the current directory
// (where the launchUri is), you can omit this option because the default value is
// ".". However, some servers do not support directory indexes so "." and "./"
// will not automatically map to the "index.html" file in that directory.
redirectUri: "index.html"
});
</script>
SMART on FHIR walkthrough
The scope
parameter specifies what kinds of data the app needs access to. See SMART on FHIR scope and lunch context for more data access options.
<script>
FHIR.oauth2.authorize({
// The client_id that you should have obtained after registering a client at
// the EHR.
//
// Note that this can be an arbitrary string when testing with
// http://launch.smarthealthit.org.
clientId: "my_web_app",
// The scopes that you request from the EHR. In this case we want to:
// launch - Get the launch context
// openid & fhirUser - Get the current user
// patient/*.read - Read patient data
scope: "launch openid fhirUser patient/*.read",
// Typically, if your redirectUri points to the root of the current directory
// (where the launchUri is), you can omit this option because the default value is
// ".". However, some servers do not support directory indexes so "." and "./"
// will not automatically map to the "index.html" file in that directory.
redirectUri: "index.html"
});
</script>
SMART on FHIR walkthrough
redirectUri
is where the EHR will redirect the web browser (client) to after authorization. In this case it is the app’s index.html
.
<script>
FHIR.oauth2.authorize({
// The client_id that you should have obtained after registering a client at
// the EHR.
//
// Note that this can be an arbitrary string when testing with
// http://launch.smarthealthit.org.
clientId: "my_web_app",
// The scopes that you request from the EHR. In this case we want to:
// launch - Get the launch context
// openid & fhirUser - Get the current user
// patient/*.read - Read patient data
scope: "launch openid fhirUser patient/*.read",
// Typically, if your redirectUri points to the root of the current directory
// (where the launchUri is), you can omit this option because the default value is
// ".". However, some servers do not support directory indexes so "." and "./"
// will not automatically map to the "index.html" file in that directory.
redirectUri: "index.html"
});
</script>
SMART on FHIR walkthrough
The EHR launch simulator has us select an encounter, which may be skipped in a production implementation.
SMART on FHIR walkthrough
index.html
As specified earlier in the redirectUri
parameter.
SMART on FHIR walkthrough
FHIR.oauth2.ready()
This access token gets embedded in a client
object to log in (authenticate) and use the established permissions (authorization) future FHIR queries.
https://hl7.org/fhir/resourcelist.html lists the FHIR resources
https://hl7.org/fhir/patient.html#resource shows the data elements in the Patient resource
FHIR format (with embedded images): https://github.com/NIH-NEI/fhir-for-research-smart-example/tree/main/synthetic_data
Based on Synthea (https://synthea.mitre.org): general purpose open source synthetic health data
If you are not coming back after the break:
Please fill out the survey:
Save these links:
Workshop materials: https://github.com/NIH-NEI/fhir-for-research-workshop
More on FHIR for Research: http://purl.org/fhir-for-research/web
Contact us: neiodshi@nei.nih.gov
https://github.com/NIH-NEI/fhir-for-research-workshop/
Generally speaking the pattern for a RESTful GET query appended to a URL will take the form of:
VERB [base]/[Resource] {?param=[value]}
GET [base]/Patient/1234
retrieves an instance of the Patient resource
MedicationRequest.subject
GET [base]/MedicationRequest?subject=1234
will get the instances of MedicationRequest for Patient/1234
MedicationRequest.subject
has a reference back to Patient, allowing us to retrieve instances if we know the patient’s IDGET [base]/Patient?name=peter
, and then a second to get the MedicationRequests for patients with that IDGET [base]/MedicationRequest?subject.name=peter
MedicationRequest.subject
can be either a Patient or Group, so this is better: GET [base]/MedicationRequest?subject:Patient.name=peter
What about “patients diagnosed with a given condition”?
Condition.subject
_has
parameter supports retrieving Patients based on a value from a Condition
:
separates fieldsCondition
)subject
)code
, which Condition uses to identify the condition)GET [base]/Patient?_has:Condition:subject:code=195662009
AND
to find john smith
: GET [base]/Patient?given=john&family=smith
OR
to find john smith
or jenny smith
: GET [base]/Patient?given=john,jenny&family=smith
FHIRPath is a path based navigation and extraction language, somewhat like XPath
Useful for extracting data from FHIR’s deeply nested data structure
JavaScript implementation: https://github.com/HL7/fhirpath.js
Try in the sandbox: https://hl7.github.io/fhirpath.js/
Get the value from Patient.gender
: Patient.gender
Get a patient’s legal last name: Patient.name.where(use='official').family
Get a patient’s MRN:
Patient.identifier.where(type.coding.system = 'http://hl7.org/fhir/v2/0203' and type.coding.code = 'MR').value
Please fill out the survey:
Save these links:
Workshop materials: https://github.com/NIH-NEI/fhir-for-research-workshop
More on FHIR for Research: http://purl.org/fhir-for-research/web
Contact us: neiodshi@nei.nih.gov
© 2024 The MITRE Corporation / Approved for Public Release / Case #24-0169