SMART Part 2: A real app in less than 100 lines

In the first article of this series, I argued that anyone building or using healthcare technology should be excited about and embracing SMART on FHIR, right now. By integrating tools and workflows directly into the EHR user experience, SMART sidesteps many of the problems that have historically stymied innovation in care delivery. It’s super cool.

However, building a truly production-ready SMART app from scratch is a big lift. Much of this comes down to excessive optionality in the standards; trying to serve every scenario makes it painful to do simple and common things. That’s where this project comes in: SmartEhr and SmartServer are real, concrete, license-free Java classes that you can use to dramatically accelerate SMART development.

Time for the rubber to meet the road. First, we’re going to build a real, production-class SMART app using this framework using one class and just under 100 lines of Java. Future articles will go deeper; for now we’re just going to get some code running and get comfortable with sandbox environments.

XrayServer

Xray was built specifically for this walkthrough, but it’s actually generally useful for investigating resources and seeing FHIR data on the wire. After going through the launch sequence, the landing page makes it easy to call FHIR resources and view the JSON output. Think about it as a baby version of Postman for SMART.

1. Get the code

Your development machine will need reasonably recent copies of the JDK (I’m using OpenJDK v11, but it should work with any JDK >= v8); Maven and Git. You probably have them already, but if not come back when you’re good. I’ll wait here.

All the code you need is in the shutdownhook repository on github. Use the green “Code” button on that page to get the code — either by cloning it or just downloading a zip and unpacking it. There are a few projects in the repository but you only care about these two:

2. Build it

Use the commands below to first build the toolbox code and install it into your local Maven repository, then build the smart-trials project. The end result is a “fat” jar in the target directory that has no external dependencies and includes main() entrypoints for XrayServer and the ClinicalTrialsServer we’ll cover later in the series.

cd YOUR-SRC-DIR/shutdownhook/toolbox
mvn clean package install
cd ../smart-trials
mvn clean package
ls -l target/smart-trials-1.0-SNAPSHOT-jar-with-dependencies.jar

If you have any problems building, let me know. I work pretty exclusively in Linux (as nature intended) so sometimes miss Mac or Windows weirdnesses. Happy to fix that up if it happened here; better yet send a pull request!

3. Run it!

The jar is bundled with a “get started” Xray configuration as well as a self-signed “localhost” certificate that allows the HTTPS server to manage browser exchanges. This makes it easy to get started with development, but obviously neither of them are production-ready. That’s ok for our purposes today.

Run the server with these commands:

cd YOUR-SRC-DIR/shutdownhook/smart-trials
java -cp \
    target/smart-trials-1.0-SNAPSHOT-jar-with-dependencies.jar \
    com.shutdownhook.xray.XrayServer

The server will start listening for requests on port 7071. Point your browser at https://localhost:7071/ and go through the motions of accepting the scary untrusted certificate — you need to do this now so that the redirect and iframe negotiations work later on. Also, if you’re running a firewall on your server, make sure to open up port 7071. On CentOS 7, I use something like:

firewall-cmd --zone=public –-permanent --add-port=7071/tcp

Now point your browser at the SMART App Launcher (https://launch.smarthealthit.org/). There’s a ton of options on this page, but just leave them alone and scroll down to the bottom. Enter https://localhost:7071/launch into the “App Launch URL” box and click “Launch App!”

MAGIC TIME. If all goes well, you’ll be prompted to select a practitioner and a patient, then land on a screen that looks like the screenshot to the right. All that noise on the page doesn’t really matter, but feel free to click around and get a feel for what FHIR resources look like. Sweet!

What Just Happened?

The App Launcher acts as a fake EHR system. By providing the /launch URL, you’re telling that EHR to open up Xray in an iframe, adding some identification parameters to the query string. Xray verifies that these look good, then redirects the browser back, saying “please give me the ok to get an access token.” The EHR ensures a patient and provider context is set up, and once again sends the browser back to Xray for some final housekeeping and the landing page.

Beyond the logistics, the most important part of this dance is agreement on the types of data that Xray requires to do its job. In the real world, this information is pre-registered with the EHR (this is skipped in the SMART App Launcher) and confirmed during the negotiation. As a testing/inspection tool, Xray makes a pretty broad request for data types, but restricts itself to “read-only” operations.

The launch negotiation is encapsulated in the launch and successfulAuth methods of SmartEhr, and specified quite well in the SMART documentation for the masochists in the audience. We just need to make sure the configuration is set up correctly.

Epic Time

The SMART App Launcher lets us skip a lot of that configuration, which is super-convenient for getting started, but leaves a bunch of gaps for apps intended for the real world. Each vendor has their own developer program which interprets the specification just a little differently. Our first target is Epic because it is by far the dominant EHR system in the United States.

Epic on FHIR is the homepage for the Epic development program. Sign up (for free), click “Build Apps” and then “Create” to set up Xray as an application in your account, using the following guide to fill out the form:

  • Application Name can be anything you like. For a production app, this information will be published in the Epic directory, but we’re never going to launch this app, so anything works.
  • Application Audience should be “Clinicians or Administrative Users.” SMART apps can work in many scenarios, but we’re focused on tools for providers, embedded in the EHR.
  • Incoming APIs is where we set our required access to data.
    • All access types are tagged with a FHIR API version: DSTU2, STU3 or R4. SmartEhr supports all of these, but we’ll just use the “R4” versions for Xray.
    • Every SmartEhr app needs at least Patient.Read access.
    • If you need information about the logged-in provider (such as their email address), you’ll need Practitioner.Read.
    • The static links on the Xray landing page include shortcuts to the patient’s problem list and medications, so also include Condition.Search (Problems) and MedicationRequest.Search (Orders).
    • Add any other data types you’re interested in as well; you can make these requests from Xray using the “Custom” input box.
  • Redirect URI should be https://localhost:7071/return (note the https:// prefix is included in the dropdown already).
  • Xray does not Require Refresh Tokens so leave this checkbox blank.

Click the “Save” button, then scroll to the bottom of the page where some new options will appear:

  • SMART on FHIR Version should be set to “R4” as discussed above.
  • Summary, like application name, is important for published apps but can be anything for Xray.

Leave the rest of the fields as they are and click “Save & Ready for Sandbox”. This will send you back to the application list; if you click on your new app, you’ll see “Client ID” and “Non-Production Client ID” fields at the top of the page. Your app is ready to go!

Before we move on, two side notes. First, there often is a delay between editing settings for your application and seeing them live in production. I have wasted a ton of time thinking my changes had no effect, only to have them light up ten or fifteen minutes later. Be patient!

It’s also worth touching on the publication process for FHIR applications so that they can be used at real Epic sites. The simplest way to get this done is do simply mark your app as ready with the “Save & Ready for Production” button. You can then share your “production” and “non-production” (for test systems) Client IDs with interested Epic customers — details are in the Epic documentation.

If you want to show up in Epic’s “App Orchard” directory of applications, life is a little more complicated (and expensive). You’ll have to join the Epic App Orchard program and go through a process whereby your application is approved for publication. You may also need to be in this program if your app needs to access data not part of the current FHIR standard — for example, at least as of this writing, billing and insurance information is only available through custom Epic APIs beyond FHIR.

We’re almost there. Epic knows about Xray, but Xray doesn’t know about Epic. Back on your development machine, fire up an editor and open the file YOUR-SRC-DIR/shutdownhook/smart-trials/src/main/resources/xray.json. In the “Sites” array, you’ll see an entry for “Epic” — replace the “ClientId” value with your “Non-Production Client ID.” Rebuild the jar and re-launch Xray to pick up the new configuration.

OK — let’s run this thing! Back on the Epic on FHIR site, use the “Documentation” dropdown and choose “Launching your App from Epic.” Click the “Try It” button, select a test patient, choose your app in the list, set the launch URL to https://localhost:7071/launch (just like with the SMART App Launcher), and click “Launch.”

MORE MAGIC TIME. The Epic sandbox doesn’t embed your app in a simulated EHR like we saw with the SMART App Launcher, but the Xray page should look very similar. This is pretty cool — the exact same code that works with the SMART App Launcher is working with Epic as well!

I’ll see your Epic and raise you a Cerner

The other behemoth in the EHR world is Cerner — most notable recently for winning a $16B contract to be the EHR for the Veteran’s Administration. Cerner also happens to be home to one of my all-time favorite developers, so woot for that.

Cerner’s SMART developer program is similar to Epic’s in a lot of ways; you can sign up for free and use their “Code Console” to set up and test applications. Once you’re logged into to the console, choose “My Apps” and then “New App” to get started. Not surprisingly, the information you provide is nearly identical to Epic — the only extra question is about OAuth2, to which you should say YES.

The interface for selecting access looks familiar too, but here we see a distinction between “User Scopes” and “Patient Scopes.” The difference is explained in the SMART specification, but in short “user” means any data the logged in user has access to (possibly including many patients), while “patient” is limited to data for the one patient that was selected in the EHR for use in your app. Usually we’re going to want the “patient” versions; an exception is user/Practitioner.read which is required to get information about the logged-in user.

Cerner has also chosen to disallow “wildcard” scopes for their FHIR applications (the SMART reference linked above talks about these), so if you choose additional data types for use in Xray, you’ll have to update the “Scopes” string in your xray.json config file to match.  

After creating your application, clicking it from the “My Apps” list will show a summary page that includes your Client Id. Copy this value and add it to the xray.json config file under Sites/Cerner. Also review the “FHIR Spec” link on that page; you may (or may not) need to update the “IssUrl” config setting with that value. Remember to re-build and re-launch Xray to pick up the new configuration.

EVEN MORE MAGIC TIME. Click on the “Begin Testing” button and use the provided demo credentials to log into the Cerner Sandbox EHR. After the same now-familiar negotiation, Xray will show up connected to Cerner data. Three for three, woo hoo!

Enough Said (for today)

As a SMART app developer, we’ve done a whole lot here with very little code. I thought “under 100 lines” was a catchy way to say that, but the meat of the app is really less than fifty — three functions that each receive authenticated SmartEhr and Session instances, ready to make FHIR requests. No muss and no fuss. In the next article, we’ll look in more detail at the launch sequence. Things are getting increasingly nerdy folks. Just the way I like it.

*** A last note and request (this will show up at the bottom of each article in the series). I’ve spent a lot of time in this industry, and the systemic impediments to progress and innovation can make even good folks feel hopeless sometimes. I really, truly believe that SMART is one of those rare technologies that has matured at exactly the right time to change the game. But there’s no guarantee — not enough folks know about it, and it’s too hard to use. If you swim in this pool, please help me fix that:

  1. Share these articles with folks that use and implement EHRs. Tell them to look at the “app store” for their system and add an app to their test system. Tell them to ask vendors if they have a SMART interface to their solution.
  2. Share these articles with folks that build care delivery solutions. Explain how they can use SMART to add functionality for customers without a custom login and without having to do an integration project with custom IT teams.
  3. Contact me if I can help. There’s a form here on the website, and I’m @seanno on Twitter, or use LinkedIn, or whatever. I’m happy to answer questions, make some connections, and heck I might even write some code for you if it makes a difference.