npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@oystehr/sdk

v3.0.3

Published

Oystehr SDK

Downloads

580

Readme

Version Compatible Node Versions Install Size Minified Size Downloads / Month License

Oystehr SDK

The Oystehr SDK is a TypeScript library for interacting with Oystehr's FHIR and Project APIs.

While you can always make raw HTTP requests to Oystehr's APIs from any language, this SDK provides several advantages for developers:

  • TypeScript types — The SDK provides complete typing for requests and responses across all of Oystehr's APIs. This gives you helpful autocomplete and build-time type checking.
  • Convenience functions — The SDK provides convenience functions for doing common tasks like uploading and downloading files with Z3.
  • FHIR Support — The SDK supports the R4B and R5 versions of FHIR and includes types and helpers for interacting with the Oystehr FHIR API.

Installation

npm install @oystehr/sdk

Usage

  1. Import the SDK.
  2. Initialize the SDK with a Oystehr access token. (Learn how to get an access token).
  3. Invoke any of Oystehr's APIs with a function call.
import Oystehr from '@oystehr/sdk';
import { Patient } from 'fhir/r4b';

const oystehr = new Oystehr({
  accessToken: 'your_access_token',
});

// Create a Patient FHIR resource
const patient = await oystehr.fhir.create<Patient>({
  resourceType: 'Patient',
});

// Upload a zambda
try {
  const zambda = await oystehr.zambda.create({ name: 'new zambda', triggerMethod: 'http_open' });
  const fileHandle = await fs.open('./path/to/my/zambda.zip')
  const file = new Blob([await fileHandle.readFile()]);
  await oystehr.zambda.uploadFile({ id: createZambda.data.id, file, })
} catch (err) {
  // Handle error thrown by Oystehr or JS in some way
}

SDK Config Object

Instantiating the SDK requires a configuration object, though all fields are optional.

interface OystehrConfig {
  /**
   * Access token for your user, M2M, or developer account.
   */
  accessToken?: string;
  /**
   * Optional Oystehr Project ID. Required for developer accessTokens.
   */
  projectId?: string;
  /**
   * Optionally provide a custom fetch implementation. This must conform to the
   * built-in node.js fetch implementation (Undici fetch).
   * @param req
   * @returns
   */
  fetch?: (req: Request) => Promise<Response>;
  /**
   * Optional configuration for retrying request in case of error. Defaults to
   * `{ retries: 3 }` if not provided.
   */
  retry?: {
    /**
     * Number of retries.
     */
    retries: number;
    /**
     * Optional, non-async function to call when a request is retried.
     * @param attempt
     * @returns
     */
    onRetry?: (attempt: number) => void;
    /**
     * Optional list of additional errors to retry for. The Oystehr SDK will always
     * retry for status codes 408, 429, 500, 502, 503, and 504.
     */
    retryOn?: number[];
  };
}

Features

Unauthenticated Usage

All Oystehr endpoints require authentication to access except for public Zambdas that you upload to your project. You can invoke these Zambdas without passing an authentication token to the SDK:

import Oystehr from '@oystehr/sdk';

const oystehr = new Oystehr({});

// Execute the Zambda
const result = await oystehr.zambda.executePublic({
  id: 'your_oystehr_zambda_id',
  additional: 'params',
});

// Do something with your Zambda result
console.log(JSON.stringify(result, null, 2));

Retries

The Oystehr SDK will retry on a variety of errors by default:

  • Common Node.js and Undici networking errors
  • Rate limiting status codes (408, 429)
  • Potentially ephemeral upstream status codes (500, 502, 503, 504)

Note that the Oystehr FHIR API is eventually consistent, so if you are retrieving or updating a resource immediately after creating it, it's recommended to retry on 404 and 412 status codes as well.

To retry on additional status codes, change the number of retries, or register a callback for each retry, pass options to the Oystehr SDK configuration object as shown above.

Types

Oystehr Types

The SDK includes input and output types for all non-FHIR Oystehr services, as well as some reusable "component" types. These types provide a single interface for inputs — no need to worry about whether a value goes into a path parameters or the request body.

import Oystehr, { UserInviteParams, UserInviteResponse } from '@oystehr/sdk';

// Hard-coded application ID from environment
const APP_ID = process.env.OYSTEHR_APP_ID;

// Elsewhere in your project, initialize an Oystehr client
// with the currently-authenticated user's access token 
const oystehr = new Oystehr({ accessToken: accessTokenFromAuthn });

// Define a reusable helper function to add practitioners
// to your application, omitting the `applicationId` field
// since you will override it.
async function inviteUserToMyApp(
  oystehr: Oystehr,
  input: Omit<UserInviteParams, 'applicationId'>
): Promise<UserInviteResponse> {
  return oystehr.user.invite({
    ...input,
    applicationId: APP_ID,
  });
}

FHIR

FHIR Types

The SDK supports both the R4B and R5 versions of FHIR from one shared set of generic functions. You shouldn't need to mix these types, since each Oystehr project can only have one FHIR version, but the example below shows how the SDK handles types for each version.

import Oystehr from '@oystehr/sdk';
import { Patient as PatientR4 } from 'fhir/r4b';
import { Patient as PatientR5 } from 'fhir/r5';

const oystehr = new Oystehr({
  accessToken: 'your_access_token',
});

const encounter4 = await oystehr.fhir.create<EncounterR4B>({
  resourceType: 'Encounter',
  status: 'arrived',
  class: {},
});
/*
 * R4B Encounters do not have `virtualService` so this shows an error:
 * Argument of type '{ resourceType: "Encounter"; status: "arrived"; class: {}; virtualService: {}; }' is not assignable to parameter of type 'Encounter'.
 *  Object literal may only specify known properties, and 'virtualService' does not exist in type 'Encounter'.ts(2345)
 */
const vs = encounter4.virtualService;
const encounter5 = await oystehr.fhir.create<EncounterR5>({
  resourceType: 'Encounter',
  /*
   * R5 Encounters have different enum values for `status` so this shows an error:
   * Type '"arrived"' is not assignable to type '"planned" | "in-progress" | "cancelled" | "entered-in-error" | "unknown" | "on-hold" | "completed" | "discharged" | "discontinued"'.ts(2322)
   */
  status: 'arrived',
  class: [],
});

FHIR Bundles

When performing a FHIR search, the resulting type is Bundle. The Oystehr SDK has a convenience function for unwrapping the bundle into an array of the base type.

import Oystehr from '@oystehr/sdk';
import { Patient } from 'fhir/r4b';
const patientBundle/*: Bundle<Patient>*/ = await oystehr.fhir.search<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: 'name',
      value: 'sam',
    },
  ],
});
const patients/*: Patient[]*/ = patientBundle.unbundle();

Batch Requests

The Oystehr FHIR service supports two kinds of batch requests: batch and transaction. In the SDK, the syntax to use these is the same.

PATCH requests behave differently in batch requests. The Oystehr SDK allows you to pass either pre-encoded Binary resources or JSON Patch operations directly.

Here's an example of a transaction that patches an appointment and then retrieves a list of today's appointments:

import dayjs from 'dayjs';
import Oystehr, { BatchInputGetRequest, BatchInputPatchRequest } from '@oystehr/sdk';
const oystehr = new Oystehr({
  accessToken: '<your_access_token>',
});
const patchAppointment: BatchInputPatchRequest = {
  method: 'PATCH',
  url: '/Appointment/some_appointment_id',
  operations: [{
    op: 'replace',
    path: '/start',
    value: dayjs().format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
  }],
};
const getAppointments: BatchInputGetRequest = {
  method: 'GET',
  url: `/Appointment?date=sa${dayjs().startOf('day').format('YYYY-MM-DDTHH:mm:ss.SSSZ')}`,
};
await oystehr.fhir.transaction({ requests: [patchAppointment, getAppointments ]});

Optimistic Locking

The Oystehr FHIR API and SDK supports FHIR's optimistic locking specification. To use optimistic locking, pass a valid FHIR version ID to the request object on your SDK function call.

import Oystehr from '@oystehr/sdk';
import { Patient } from 'fhir/r4b';

const oystehr = new Oystehr({
  accessToken: 'your_access_token',
});

// Create a Patient FHIR resource
const patient = await oystehr.fhir.create<Patient>({
  resourceType: 'Patient',
});

// Fails if patient has been updated by a separate process
const updatedPatient = await oystehr.fhir.update<Patient>({
  resourceType: 'Patient'
  id: patient.id,
  active: true,
}, { optimisticLockingVersionId: patient.meta?.versionId });

Convenience Functions

Z3

Under the hood, uploading and downloading files with Z3 is a two-step process:

  1. Create a presigned URL to upload or download the file (API Reference).
  2. Upload or download the file using the presigned URL.

The SDK provides convenience functions which combine these steps into a single function call to make it a one-liner:

    await oystehr.z3.uploadFile(
      { bucketName: 'your-bucket-name', 'objectPath+': 'path/to/your-filename', file: someFileBlob }
    );

In the example, someFileBlob is a Blob.

    const downloadedBuffer = await oystehr.z3.downloadFile(
      { bucketName: 'your-bucket-name', 'objectPath+': 'path/to/your-filename' }
    );

Zambda

Uploading code for your Zambda Functions is very similar to uploading a file with Z3. After creating your .zip archive, use the uploadFile() function to deploy your code.

  const zambdaFile = fs.readFileSync('build/your-zambda-code.zip');
  await oystehr.zambda.uploadFile({
    id: yourZambdaId,
    file: new Blob([zambdaFile]),
  });

Migration Guide

If you have questions about migrating to a new version of the Oystehr SDK, check out our migration guide.

Contact

Questions? Join our public Slack or contact support.