Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: API Calls and FHIR #13

Merged
merged 1 commit into from Sep 7, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
76 changes: 76 additions & 0 deletions text/0013-api-calls-and-fhir.md
@@ -0,0 +1,76 @@
# API Calls and FHIR
- Start Date: 2019/07/02
- RFC PR: https://github.com/openmrs/openmrs-rfc-frontend/pull/13

## Decision, including impact on distributions
All API calls from the browser to the server go through the `@openmrs/api` javascript
module.

The `@openmrs/api` provides the following:
- `authenticatedFetch`
- `fhir`
- `getCurrentPatient`, and a few similar `getCurrent...` functions for shared data.

Whenever a FHIR backend API is available, it should be used. When no such FHIR api exists,
the OpenMRS-specific APIs will be used.

## Definition
- `fhir` refers to the [official fhir.js npm package](https://github.com/FHIR/fhir.js/). [FHIR itself](https://www.hl7.org/fhir/overview.html)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have we considered using the java based HAPI FHIR APIs by James Agnew & team, https://github.com/jamesagnew/hapi-fhir
Their project seems to be more active than the fhir.js one

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. We should evaluate which library to use. However, cautioning again on the common model, which i am sure will be difficult to agree to! (e.g. Patient attributes, would probably need specific extensions, or relationships etc)
I am hoping that we use "Adapters"/"proxies" for FHIR as discussed before. So if the current patient fhir model (provided by openmrs), I should have ability to override that and fetch from a different api (as long as we agree to return a min common agreed model).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gregoryjschmidt Java doesn't work in the browser. Since HAPI is a java library, we can't use it in the browser. Also, HAPI is for integrating with your database, which is not a frontend concern.

The decision about HAPI is one for the backend, not the frontend.

Also, I did research into fhir.js and smart on fhir client-js. They are both well maintained. Their code is good. It has been updated recently. I'm not sure why you are saying fhir.js isn't well maintained, because it seems well maintained to me. I've been impressed with the project in the research and experimentation that I did, including installing the library and trying it out.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@angshu without a common model it's impossible to prevent duplicate network requests. I very much agree with you that the common model is troublesome and won't work for many use cases. For the code that doesn't want to use the common model, they can use authenticatedFetch or fhir to call whichever endpoint they want to however they want to (instead of getCurrentPatient which provides only the common model). Doing that causes duplicate network requests, but that is okay sometimes.

is an acronym that refers to the industry standard API specification for healthcare data. At the time of this writing, the OpenMRS
backend largely offers only non-FHIR APIs for the frontend to use.
- Within a browser, an API call is an HTTP request to the server. This can be done with either
[fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) or
[XMLHttpRequest XHR](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest).
Fetch is preferred when possible, since it is newer and easier to work with (uses Promises).
- Authentication of an API call is accomplished through a [`Cookie` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
or other [HTTP headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers). The `@openmrs/api` wrapper for fetch
adds any needed headers and handles server responses that indicate the user is not logged in.
- `getCurrentPatient` and other `getCurrent...` functions are provided to share data between disparate microfrontends that
need the same data at the same time. They use the frontend route to identify the current patientUuid and then maintain a cache
for the shared object. The cache is busted whenever the url changes such that the patientUuid is no longer in the frontend url.

## Reason for decision
### `authenticatedFetch`

Maintaining an OpenMRS-specific wrapper around `window.fetch()` allows us to make a single code change when
improving or fixing authentication and other API behavior.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joeldenning Not sure how this would work out, given that zone.js already has wrapped window.fetch()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since authenticated fetch calls window.fetch, zone.js will still be able to make the fetch promise a “Zone Aware promise” and trigger an Angular re-render when the fetch result comes back.

For angular applications, it might be best to inject our own HttpProvider that calls authenticated fetch instead of XHR or window.fetch. See “common practices section” where that is called out.


### `fhir`

FHIR is the industry standard for healthcare data. The more that OpenMRS uses FHIR, the more interoperable and
accessible it is to researchers and software systems.

The `fhir` variable exported by `@openmrs/api` is [documented here](https://github.com/FHIR/fhir.js/#native-adapter-npm-install-fhirjs).
It is a pre-configured fhirjs instance that uses `authenticatedFetch` under the hood. When FHIR apis are available,
`fhir` should be used instead of `authenticatedFetch`.

### `getCurrentPatient`

Pages within the patient chart need a shared model of the patient object in order to avoid duplicate network requests
to get the patient object.

Providing a route-based cache of the patient object is a way to avoid duplicate network requests without coupling
components and microfrontends together.

## Alternatives

### `fhir`

Instead of using fhirjs, we could use [Smart on FHIR's client-js](https://github.com/smart-on-fhir/client-js). Smart on FHIR
is a [FHIR Profile](https://www.hl7.org/fhir/profiling.html) and set of software tools funded by the United States that offers
a regional FHIR profile for North America. [From the Smart on FHIR website](http://docs.smarthealthit.org/profiles/):

> To support apps that run unmodified across different health IT systems, we need a set of “ground rules” that define which data fields are required vs. optional, and which coding systems should be used in a given context. The FHIR specification leaves many of these decisions open to downstream implementers, to ensure that FHIR can work with a variety of use cases. But for a viable app platform, we need more.
>
> As much as possible we want to avoid inventing these “ground rules” ourselves. In the United States, SMART has adopted the profiles outlined in the Argonaut Implementation Guide. Similarly, communities in other regions should work together to define a standard set of profiles that are appropriate for the terminology systems commonly used in their area.

Since the OpenMRS backend FHIR implementation is not specific to the United States nor the Smart on FHIR profile, fhirjs was chosen instead
of Smart on FHIR's client-js.

## Common practices (not enforced)
- Whenever possible, use the `fhir` variable to make network requests.
- Do not add `getCurrent...` functions to avoid all possible duplicate API requests. In a complicated frontend that is configurable
and extensible, it is impractical to prevent all network request duplication.
- `authenticatedFetch` must be completely compatible with any valid call to `window.fetch()`
- Do not use [`axios`](https://github.com/axios/axios), [`Angular HTTP client`](https://angular.io/guide/http), or other API call
libraries without first configuring them to call `authenticatedFetch` for all network requests.