Skip to content

Commit

Permalink
Updated docs to use the new client API #175
Browse files Browse the repository at this point in the history
  • Loading branch information
vlad-ignatov authored Nov 18, 2019
1 parent 0493a40 commit df4fb98
Showing 1 changed file with 149 additions and 68 deletions.
217 changes: 149 additions & 68 deletions tutorials/javascript/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,88 +12,169 @@ apps that interact with a FHIR REST API server. It can help your app get
authorization tokens, provide information about the user and patient record in
context, and issue API calls to fetch clinical data.

This tutorial describes how to create a basic client-side JavaScript SMART app with
no additional libraries and frameworks.

To get started with the SMART on FHIR JavaScript client library, you'll need to:

### 1. Include a `script` tag
### Step 1. Include a `script` tag

Include a `script` tag referencing the library. The latest code is always
available *for download* (not live hosting) in GitHub at
[https://raw.githubusercontent.com/smart-on-fhir/client-js/master/dist/fhir-client.js](https://raw.githubusercontent.com/smart-on-fhir/client-js/master/dist/fhir-client.js).

You'll want to download and host this file alongside your app. Unless you're
just prototyping -- in which case you can use this [rawgithub
link](https://rawgithub.com/smart-on-fhir/client-js/master/dist/fhir-client.js)
in your script tag's `src`. (Be sure never to use "rawgithub" in a deployment scenario, though!)

Note: if prototyping in [JsFiddle](https://jsfiddle.net), then you will need to use the [CDN hosted](https://cdn.rawgit.com/smart-on-fhir/client-js/v0.1.8/dist/fhir-client.js) version.

available from the NPM CDN at
[https://cdn.jsdelivr.net/npm/fhirclient/build/fhir-client.js](https://cdn.jsdelivr.net/npm/fhirclient/build/fhir-client.js).
Including this script will create a global `FHIR` object for you to work with.

### 2. Create or obtain an instance of `FHIR.client`

In a typical workflow, you won't instantiate this object yourself -- it will
be created by `FHIR.oauth2.launch()`. But when you're prototyping, you can just
create your own. By convention, we call this instance `smart`. So you can
create a client via:

```js
var smart = FHIR.client({
serviceUrl: 'https://r2.smarthealthit.org',
patientId: 'smart-1137192'
});
**Example:**
```html
<script src="https://cdn.jsdelivr.net/npm/fhirclient/build/fhir-client.js"></script>
```

This object will be your touchpoint for making FHIR API calls. Your client
will have some context built in, including:

* `smart.patient`
* `smart.user`

### 3. Use your client instance to execute FHIR API calls
### Step 2. Test against an open FHIR server
Now that you have the client library included, it might be a good idea to play
with it a little bit and see what it is capable of. This is easy to do if we use
an open FHIR server that does not require authentication.

You can make calls like:

* `read`: fetch a single resource, given its id
* `search`: search for resources that match a set of criteria

In general, you can make API calls that are scoped to the current patient by
using `smart.patient`, as follows:
Here is something basic to start with:

**Connect to an open server and browse patients**
```js
// Search for the current patient's conditions
smart.patient.api.search({type: 'Condition'});

// Search for the current patient's prescriptions
smart.patient.api.search({type: 'MedicationOrder'});
const client = FHIR.client("https://r3.smarthealthit.org");
client.request("Patient").then(console.log).catch(console.error);
```

If you're writing a population-level app, you can query across patient records
by using `smart.api`, as follows:

```js
// Search for conditions added today
var todaysDiagnoses = smart.api.search({type: 'Condition', query: {dateRecorded: '2014-05-01'}});

// Search for all statins prescribed today
var statinRxs = smart.api.search({type: 'MedicationOrder', query: {dateWritten: '2014-05-01', name: 'statin'}});
Once this is working you can try [other examples](http://docs.smarthealthit.org/client-js/request.html)
and then proceed to the next step.

### Step 3. SMART Authorization
The most common type of SMART app is designed to run within the EHR. Such an app must
support the [EHR Launch flow](http://www.hl7.org/fhir/smart-app-launch/#ehr-launch-sequence). To do that, we need to separate our logic in two pages:
1. `launch.html` - to be called by the EHR to start the authorization process.
2. `index.html` - Upon successful authorization the EHR will redirect us there. This is where the actual app is initialized.

### Step 3.1. Create the launch.html page
This is very simple blank page with one purpose - to launch the app:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Launch My APP</title>
<script src="https://cdn.jsdelivr.net/npm/fhirclient/build/fhir-client.js"></script>
</head>
<body>
<script>
FHIR.oauth2.authorize({
// The client_id that you should have obtained after registering a client at
// the EHR.
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>
</body>
</html>
```

These functions return `$.Deferred` objects, which you can work with simply by
calling their `done` method:

```js
statinRxs.done(function(prescriptions){
console.log(prescriptions);
});
Note that you cannot just open this in the browser to launch the app. Instead, the EHR will open
this page and will pass some URL parameters. Without those parameters the launch will not work.
Alternatively, you can use a launcher like http://launch.smarthealthit.org/ to pretend that you
are the EHR and launch your app with sample patients data.

At this point you need to make sure your app is hosted on an HTTP server. If you have NodeJS installed,
the simplest way to host these files would be to `cd` into your project folder (where the `launch.html`
is saved) and run `npx http-server`.

### Step 3.2. Create the index.html page
After the launch sequence is started we will be redirected to the EHR where we may have to authorize
the launch and select a patient (depending on the requested scopes). Once that is complete, the EHR
will redirect back to our site using the `redirectUri` option provided above. Once we are there,
we have to complete the authorization flow, which the client library will do for us. Then we can start
making FHIR request and implement our business logic. Here is an example of simple app that renders as JSON
the current patient and his/her medications (if any):
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Example SMART App</title>
<script src="https://cdn.jsdelivr.net/npm/fhirclient/build/fhir-client.js"></script>
<style>
#patient, #meds {
font-family: Monaco, monospace;
white-space: pre;
font-size: 13px;
height: 30vh;
overflow: scroll;
border: 1px solid #CCC;
}
</style>
</head>
<body>
<h4>Current Patient</h4>
<div id="patient">Loading...</div>
<br/>
<h4>Medications</h4>
<div id="meds">Loading...</div>
<script type="text/javascript">
FHIR.oauth2.ready().then(function(client) {
// Render the current patient (or any error)
client.patient.read().then(
function(pt) {
document.getElementById("patient").innerText = JSON.stringify(pt, null, 4);
},
function(error) {
document.getElementById("patient").innerText = error.stack;
}
);
// Get MedicationRequests for the selected patient
client.request("/MedicationRequest?patient=" + client.patient.id, {
resolveReferences: [ "medicationReference" ],
graph: true
})
// Reject if no MedicationRequests are found
.then(function(data) {
if (!data.entry || !data.entry.length) {
throw new Error("No medications found for the selected patient");
}
return data.entry;
})
// Render the current patient's medications (or any error)
.then(
function(meds) {
document.getElementById("meds").innerText = JSON.stringify(meds, null, 4);
},
function(error) {
document.getElementById("meds").innerText = error.stack;
}
);
}).catch(console.error);
</script>
</body>
</html>
```

In the `done` callback, you'll get a data structure containing search results.

Here's a complete example of the steps above:
Now you can launch this against different EHRs or sandboxes. To do so, you will
have to "register" the app, get it's client ID and put it in the `clientId` field
in your `launch.html`. The easiest way to try this would be to use the SMART sandbox
at http://launch.smarthealthit.org which does not even require you to register
the app. Furthermore, if your app is running on http://127.0.0.1:8080/, then you
can launch it simply by clicking [HERE](http://127.0.0.1:8080/launch.html?launch=eyJhIjoiMSJ9&iss=https%3A%2F%2Flaunch.smarthealthit.org%2Fv%2Fr4%2Ffhir).

<iframe
style="border: 1px solid black"
src="https://embed.plnkr.co/lnMOo6nsHVkVeF9gnLka/"
width="100%" height="500px"></iframe>

For more details, see our [JS client docs](../../clients/javascript)
For more details, see our [JS client docs](http://docs.smarthealthit.org/client-js/)

0 comments on commit df4fb98

Please sign in to comment.