CONTROLLED DATA
Leidos Proprietary - US Citizens ONLY
The information contained herein is proprietary to Leidos, Inc. It may not be used, reproduced, disclosed, or exported without the written approval of Leidos.

This guide shows the steps required to make a basic SMART on FHIR application that uses Terra UI with internationalization that can connect to the Cerner sandbox.  The steps start from nothing and do not use anything existing code from source control.  Links provide additional information on the step but are not required reading.

Step-by-step guide

Prerequisites

Node.js

Node is required for Node Package Manager (npm) and Node package runner (npx).  Node.js is available through CIO Central.

Visual Studio Code

Any IDE can be used but Visual Studio Code is recommended.  Code is not available on CIO Central.  It technically requires approval but it will install on a Leidos laptop without admin rights.

Step-by-step guide

Register account with Cerner Code

  1. https://code.cerner.com/developer/smart-on-fhir/apps
  2. Select "Create an account"
  3. Fill in information
  4. The process is automated but it may take a few minutes before the account is fully ready.

Create base React application

  1. Open a terminal window
  2. Navigate to the desired folder for the project

  3. > npx create-react-app myapp

  4. > cd myapp

Install React Router

The FHIR launch sequence requires a launch page and a main entry point for the application.  React Router provides an easy way to have two entry points.
https://www.npmjs.com/package/react-router-dom
https://codesandbox.io/s/fhir-client-react-react-router-context-0q3n8

  1. > npm i react-router-dom

Install FHIR client

https://www.npmjs.com/package/fhirclient
http://docs.smarthealthit.org/client-js/
https://engineering.cerner.com/smart-on-fhir-tutorial/

  1. > npm i fhirclient

Install Terra UI base components

Two base level components are required in any Terra UI project.
https://engineering.cerner.com/terra-ui/getting-started/terra-ui/what-is-terra

  1. > npm i terra-base terra-aggregate-translations

Install terra-base peer dependencies

React Intl is required by Terra UI components to do internationalization.
https://www.npmjs.com/package/react-intl

  1. > npm i react-intl@^2.8.0
  2. > npm i node-sass

Install Terra UI components

Install the components which will actually make up the UI.  A full list of components with examples can be found in the link below.  For this example we will only add terra-demographics-banner.
https://engineering.cerner.com/terra-ui/components

  1. > npm i terra-demographics-banner

Enable local environment variables

Every developer will likely have a different clientId.  This sets up a framework for holding local development variables.

https://create-react-app.dev/docs/adding-custom-environment-variables/#adding-development-environment-variables-in-env

  1. Add file ./.env.  This is defaults for all variables and will be in source control.
  2. Add file ./.env.local.  This is local overrides and it is listed in .gitignore by default.
  3. Enter the following text in both files.

    .env / .env.local

    REACT_APP_CLIENT_ID=clientId not entered

Add aggregate-translations script to start scripts

This adds a step to the start where translations are built.  In this example I only have English, German, and Spanish.

WARNING: The -o parameter is a hack.  See "Resolving Translations and Loaders" in the link below.  We need to have access to the webpack config but it is hidden by CRA.  Could eject or find a different solution.

https://github.com/cerner/terra-aggregate-translations/blob/master/README.md

  1. Open package.json
  2. Add the prestart and aggregate-translations line from the example below

package.json

  "scripts": {
    "aggregate-translations""tt-aggregate-translations -d ./translations -l ['en','es','de'] -o ./node_modules/terra-i18n/node_modules -f es6",
    "prestart""npm run aggregate-translations",
    "start""react-scripts start",
    "build""react-scripts build",
    "test""react-scripts test",
    "eject""react-scripts eject"
  },

Edit index.html

Terra internationalization requirement.

  1. Edit ./public/index.html
  2. Change html tag from <html lang="en"> to <html dir="ltr" lang="en">

Create index.js

index.js is the entry point into the application.  It is only doing routing.

  1. Delete all files under ./src but leave the directory
  2. Add file ./src/index.js
  3. Copy the contents below

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouterRoute } from "react-router-dom";

import Launcher from "./Components/Launcher";
import App from "./Components/App";

ReactDOM.render(
    <BrowserRouter>
        <Route path="/app" component={App} />
        <Route path="/" component={Launcher} exact />
    </BrowserRouter>
    , document.getElementById('root'));


Create Launcher.js

  1. Create folder ./src/Components
  2. Create file ./src/Components/Launcher.js
  3. Paste in contents below

Launcher.js

import React from "react";
import { oauth2 as SMART } from "fhirclient";


export default class Launcher extends React.Component {

    componentDidMount() {
        SMART.authorize({
            clientId: process.env.REACT_APP_CLIENT_ID,
            scope: "patient/Patient.read launch online_access openid profile",
            redirect_uri: './app'
        });
    }

    render() {
        return "Launching...";
    }
}


Create App.js

  1. Create file ./src/Components/App.js
  2. Paste in the contents below

App.js

import React, { useStateuseEffect } from 'react';
import { oauth2 as SMART } from "fhirclient";
import DemographicsBanner from 'terra-demographics-banner';
import Base from 'terra-base'
import { FormattedMessage } from 'react-intl';


const locales = ['en''es''de'];

const App = () => {
    const [patientsetPatient] = useState({});
    const [localesetLocale] = useState((navigator.languages && navigator.languages[0])
        || navigator.language
        || navigator.userLanguage
        || 'en');

    useEffect(() => {
        SMART.ready().then(function (smart) {
            console.log('SMART.ready successful');
            smart.patient.read()
                .then((p=> {
                    console.log('Patient retrieved'p);
                    setPatient(p);
                }, (err=> {
                    console.log(err);
                });
        }, function (err) {
            console.log(err);
        });
    }, []);

    return (
        <Base locale={locale}>
            <div>
                <DemographicsBanner
                    dateOfBirth={patient.birthDate ? patient.birthDate : '--'}
                    gender={patient.gender ? patient.gender : '--'}
                    identifiers={MRN: 12343REA: '3JSDA' }}
                    personName={patient.name ? patient.name[0].text : '--'}
                />
            </div>
            <h1><FormattedMessage id="MyApp.hello" /></h1>
            <p>
                <label htmlFor="locale-switcher">Select locale</label>
                <select
                    id="locale-switcher"
                    name="selectLocale"
                    value={locale}
                    onChange={(event=> { setLocale(event.target.value) }}
                >
                    {locales.map((item=> <option key={item} value={item}>{item}</option>)}
                </select>
            </p>
        </Base>
    );
}

export default App;


Create Translations

  1. Create folder ./translations
  2. Create files en.json, de.json, and es.json
  3. Paste in the contents below

en.json

{
    "MyApp.hello""Hello World"
}


de.json

{
    "MyApp.hello""Hallo Welt"
}


es.json

{
    "MyApp.hello""Hola Mundo"
}


Register the app with Cerner

  1. Go to https://code.cerner.com/developer/smart-on-fhir/apps
  2. Select New App
  3. Enter
    1. App Name: (any name)
    2. SMART Launch URI: http://localhost:3000/
    3. Redirect URI: http://localhost:3000/app
    4. App Type: Provider
    5. FHIR Spec: r4
    6. Authorized: Yes
    7. Standard Scopes: all
    8. User Scopes: none
    9. Patient Scopes: Patient.read
  4. Select Register
  5. Note the client ID

Update clientID env variable

  1. ./.env.local
  2. Replace "clientId not entered" with the value you got from Cerner registration

Test the app

  1. Bring up the terminal
  2. > npm start
  3. Wait for the app to build
  4. Go back to Cerner Code and select your app
  5. Hit Begin Testing and follow prompts