SMART on FHIR JavaScript Client

repository·master·Indexed 43 days ago

https://github.com/smart-on-fhir/client-js

A cross-platform JavaScript library (version 2.6.2) designed to facilitate connections between SMART applications and FHIR servers. It supports browser environments, including legacy IE11, and Node.js, providing a SMART API for authorization and a Client for performing FHIR CRUD operations and querying resources via the request() method.

Tokens
21.5K
Snippets
78
Records
89
Agent score
56%

What's inside SMART on FHIR

  1. Implement custom session storage

    master

    If your request/response objects do not have a session property, or if you want to use an alternative storage mechanism, you must provide a custom storage object.

    Your storage object must implement the following interface:

    • get(key: string): Promise<any>: Returns the value or undefined.
    • set(key: string, value: any): Promise<any>: Returns the value.
    • unset(key: string): Promise<Boolean>: Returns true on success.

    You can pass the storage object directly, or better yet, pass a storage factory function that receives { request, response } to allow for request-specific storage instantiation.

    // Using a storage factory function (recommended)
    function createStorage({ request, response }) {
        return new AwesomeCustomStorage(request, response);
    }
    
    // inside a route handler
    smart(request, response, createStorage).authorize(options)
  2. Access the SMART API in Browser or Node.js

    master

    The SMART API provides methods for app authorization and launch (authorize, ready, init).

    • In the Browser: The API is automatically available at window.FHIR.oauth2.
    • In Node.js: The library exports a function that accepts request and response objects and returns the SMART API instance.

    Important: When using authorize(), the browser will redirect to the authorization server. Do not add code to the returned promise chain that expects to execute immediately after the call, as the redirect may prevent execution.

    // BROWSER
    const smart = FHIR.oauth2;
    smart.authorize(options);
    
    // SERVER
    const smart = require("fhirclient");
    smart(request, response).authorize(options);
  3. Use `FhirClient` for non-SMART FHIR requests

    master

    Since version 2.6.0, the library has introduced a FhirClient base class. This class handles standard FHIR HTTP requests without requiring SMART authorization or launch context.

    The standard Client instance returned after a successful SMART authorization is a subclass of FhirClient. If you are building an application that only interacts with open FHIR servers and does not need SMART features, you should use FhirClient directly to avoid unnecessary overhead.

  4. Configure resolveReferences in client.request()

    master

    The resolveReferences option allows you to specify paths to references within a resource that should be automatically fetched and 'mounted' into the result object.

    • Paths: Relative to the current resource (e.g., subject instead of entry.resource.subject).
    • Depth: Can be arbitrary depth (e.g., a.b.c), but should not include the final .reference segment.
    • Arrays: Use index for specific items (e.g., generalPractitioner.0) or omit index to resolve all items in the array.
    • Loops: Use .. once to match all elements in an array (e.g., identifier..assigner).
    • Graphing: By default (graph: true), resolved references replace the original reference string in the result tree. If graph: false, the method returns an object with { data, references }, where references is a map of the resolved objects.
    // Resolves with augmented Encounter
    client.request("Encounter/123", {
        resolveReferences: [ "serviceProvider" ]
    });
    
    // Resolves with { data, references } instead of mounting
    client.request("Encounter/123", {
        resolveReferences: [ "serviceProvider" ],
        graph: false
    });
  5. Understand the SMART API and Client abstractions

    master

    The library provides two primary layers of interaction:

    1. The SMART API

    This is used for application authorization and launch. It provides methods like authorize(), ready(), and init().

    • In the Browser: Available at window.FHIR.oauth2.
    • In Node.js: The library exports a function that accepts (request, response) and returns the SMART API.
    // BROWSER
    const smart = FHIR.oauth2;
    smart.authorize(options);
    
    // SERVER
    const smart = require("fhirclient");
    smart(request, response).authorize(options);

    2. The Client

    Returned by ready() or init(), the Client is used to interact with the FHIR server. It features a powerful request() method for querying FHIR resources.

    You can also instantiate a client manually for open servers:

    // BROWSER
    const client = FHIR.client({
        serverUrl: "https://r4.smarthealthit.org"
    });
    
    // SERVER
    const client = fhirClient(req, res).client({
        serverUrl: "https://r4.smarthealthit.org"
    });
  6. Bypass authentication for testing

    master

    You can bypass the full SMART authentication flow by providing the fhirServiceUrl option in FHIR.oauth2.authorize(). This is useful for testing and will cause authorize to redirect immediately to your redirectUri.

    Behavior based on server type:

    • Open Server: If fhirServiceUrl points to an open server, it will work regardless of the fakeTokenResponse.
    • Protected Server:
      • If you provide an arbitrary access_token in fakeTokenResponse, requests will likely fail due to an invalid Authorization header.
      • If you do not provide an access_token, requests will fail because the Authorization header will be missing.

    Precedence: An iss option or URL parameter takes precedence over fhirServiceUrl. If an iss parameter is present (e.g., when launched by an EHR), the fhirServiceUrl option will be ignored.

    FHIR.oauth2.authorize({
        redirectUri: "./index.html",
        fhirServiceUrl: "https://r4.smarthealthit.org",
        fakeTokenResponse: {
            access_token: "generate access token somehow"
        }
    })
  7. Update FHIR.oauth2.ready() to use Promises in v2+

    master

    In fhirclient v2+, FHIR.oauth2.ready() returns a Promise. While the old callback signature ready(onSuccess, onError) is still supported and converted internally, it is recommended to use the Promise-based syntax for clarity and better control over the execution chain.

    Recommended usage:

    FHIR.oauth2.ready().then(onSuccess).catch(onError);

    Key changes to callback logic:

    • onError is now a promise rejection handler. It will always receive a single argument: an Error instance.
    • onSuccess is part of the promise chain. If you throw an error or return a rejected promise within onSuccess, it will propagate to the onError handler.
    • You can return a Promise from onSuccess, and it will be awaited.
  8. Integrate fhir.js with the SMART client

    master

    The library does not include fhir.js by default. If you need its MongoDB-like query syntax or legacy API support (client.api.*), you must include it manually.

    Browser Integration

    Include fhir.js (e.g., nativeFhir.js) via a script tag before the client. The library will automatically detect it.

    <script src="/path/to/nativeFhir.js"></script>
    <script src="/path/to/fhir-client.js"></script>
    <script>
    FHIR.oauth2.ready().then(client => client.api.search({ type: "Patient" }))
    </script>

    Node.js Integration

    In Node.js, you must manually link fhir.js using the client.connect() method.

    const fhirJs = require("fhir.js");
    
    app.get("/", async (req, res) => {
        const client = await smart(req, res).ready();
        client.connect(fhirJs);
        client.api.search({ type: "Patient" }).then(res.json).catch(res.json);
    });
  9. Install the SMART on FHIR JavaScript Library

    master

    You can install the library via NPM or include it directly in the browser using a CDN.

    NPM

    npm i fhirclient

    CDN

    Include a <script> tag pointing to one of the following:

    Latest version:

    • https://cdn.jsdelivr.net/npm/fhirclient/build/fhir-client.js
    • https://cdn.jsdelivr.net/npm/fhirclient/build/fhir-client.min.js

    Specific version (e.g., 2.0.7):

    • https://cdn.jsdelivr.net/npm/fhirclient@2.0.7/build/fhir-client.js
    • https://cdn.jsdelivr.net/npm/fhirclient@2.0.7/build/fhir-client.min.js
  10. Initialize the SMART FHIR Client

    master

    The FHIR client is the primary interface for interacting with a FHIR server. It is typically obtained from the ready() call of the SMART API. You can also instantiate it manually depending on your environment.

    In the Browser: Use FHIR.client(baseUrl).

    On the Server (Node.js): Use smart(req, res).client(baseUrl).

    // BROWSER
    const client = FHIR.client("https://r4.smarthealthit.org");
    
    // SERVER
    const client = smart(req, res).client("https://r4.smarthealthit.org");
  11. Authorize using a custom popup or iframe

    master

    To avoid popup blockers or use custom UI (like an iframe with animations), you can pass a function to the target option. This function should return a string (the frame name), a Window object, or a Promise that resolves to one of those values.

    // Example: Using a function that resolves to an iframe name after an animation
    function openPopup() {
        return new Promise(resolve => {
            // Start opening animation
            document.getElementById("layer-popup").classList.add("open");
    
            // Reset the iframe
            document.getElementById("myPopupFrame").src = "about:blank";
    
            // Resolve with the frame name once transitions are done
            setTimeout(() => resolve("myPopupFrame"), 1000);
        });
    }
    
    FHIR.oauth2.authorize({
        target: openPopup,
        // ...
    });