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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@joseantcordeiro/smart-client

v0.1.1

Published

A reusable TypeScript package for fhir client authorization within a backend application.

Readme

@joseantcordeiro/smart-client

SMART FHIR Backend Services Client

This package provides a TypeScript client for backend applications to securely authenticate and interact with SMART on FHIR servers. It implements the SMART App Launch IG: Backend Services profile, specifically focusing on asymmetric (public key) client authentication (private_key_jwt) using the OAuth 2.0 client credentials grant flow.

The client handles fetching the server's SMART configuration, generating JWT assertions for authentication, obtaining access tokens, and making FHIR API requests.

Features

  • SMART Backend Services Authentication: Implements private_key_jwt client authentication.
  • Asymmetric Key Support: Uses RSA or ECDSA private keys (PKCS8 PEM or JWK format) to sign JWT assertions.
  • Automatic Token Management:
    • Fetches and caches access tokens.
    • Handles token expiration and renewal.
  • SMART Configuration Discovery: Retrieves server capabilities from the .well-known/smart-configuration endpoint.
  • FHIR API Interaction:
    • Provides methods for common FHIR operations (GET, POST, PUT, DELETE, PATCH).
    • Includes automatic retries for requests failing with 401/403 (Unauthorized/Forbidden) after refreshing the token.
  • Typed Interfaces: Includes TypeScript types for configuration, server metadata, and token responses.
  • Error Handling: Custom error classes for better diagnostics of initialization, authentication, and request failures.
  • Flexible Configuration: Allows detailed configuration of client ID, scopes, private keys, signing algorithms, etc.

Installation

Prerequisites

  • Node.js (>=18.x)
  • npm, pnpm, or Yarn
  • A provisioned client ID and registered public key(s) with the target FHIR authorization server.

Steps

  1. Add the package as a dependency to your application:

    pnpm add @joseantcordeiro/smart-client
    pnpm add -D @types/fhir

Usage

Configuration

The SmartClient requires a configuration object of type SmartClientConfig. This configuration typically comes from a secure source, like environment variables or a database, specific to the organization using the client.

// src/types.ts (example of what SmartClientConfig looks like)
export interface SmartClientConfig {
  clientId: string; // Your client_id
  iss: string; // Issuer URL
  scope: string; // e.g., "system/Patient.read system/Observation.write"
  privateKey: string; // Your private key in PEM (PKCS8) or JWK (JSON string) format
  fhirBaseUrl?: string; // Base URL of the FHIR server (e.g., "https://fhir.your-server.com/r4")
                        // Used for FHIR requests and optionally for .well-known discovery
  kid?: string; // Optional: Key ID for the public key, if registered with the FHIR server
  signingAlgorithm?: 'RS384' | 'ES384' | string; // Optional: JWA algorithm, defaults to 'RS384'
  jwksUrl?: string; // Optional: URL to your public JWK Set
  jwtLifetime?: number; // Optional: Lifetime of the generated JWT assertion in seconds (default: 300)
}

Initialization

The client must be initialized before use. The static init() method fetches the FHIR server's SMART configuration.

import { SmartClient, SmartClientConfig } from '@joseantcordeiro/smart-client';

async function initializeSmartClient() {
  const config: SmartClientConfig = {
    clientId: process.env.FHIR_CLIENT_ID!,
    iss: process.env.FHIR_CLIENT_ID!,
    scope: 'system/*.read',
    privateKey: process.env.FHIR_PRIVATE_KEY!, // Load your private key securely
    fhirBaseUrl: process.env.FHIR_SERVER_BASE_URL, // e.g., "https://sandbox.fhir.org/r4"
    kid: process.env.FHIR_KID, // Optional
    // signingAlgorithm: 'ES384', // Optional
  };

  try {
    // Option 1: Discover .well-known/smart-configuration using fhirBaseUrl's origin
    const client = await SmartClient.init(config);

    // Option 2: Provide an explicit issuer URL for .well-known/smart-configuration discovery
    // const explicitIssUrl = 'https://auth.example.com/auth/realms/myrealm';
    // const client = await SmartClient.init(config, explicitIssUrl);

    console.log('SmartClient initialized successfully.');
    return client;
  } catch (error) {
    console.error('Failed to initialize SmartClient:', error);
    // Handle initialization error (e.g., server unreachable, invalid config)
    throw error;
  }
}

Making FHIR API Requests

Once initialized, you can use the client instance to make FHIR API requests. The client handles token acquisition and renewal automatically.

async function fetchPatientData(client: SmartClient, patientId: string) {
  try {
    // Example: Fetch a Patient resource
    const patient = await client.get(`Patient/${patientId}`);
    console.log('Fetched Patient:', patient);

    // Example: Search for Observations for this patient
    const observations = await client.get('Observation', {
      params: { patient: patientId, _count: 10 },
    });
    console.log('Fetched Observations:', observations);

    // Example: Create a new Observation
    const newObservation = {
      resourceType: 'Observation',
      status: 'final',
      code: { coding: [{ system: 'http://loinc.org', code: '29463-7' }] }, // Example: Body Weight
      subject: { reference: `Patient/${patientId}` },
      valueQuantity: { value: 70, unit: 'kg', system: 'http://unitsofmeasure.org', code: 'kg' },
      effectiveDateTime: new Date().toISOString(),
    };
    // const createdObservation = await client.post('Observation', newObservation);
    // console.log('Created Observation:', createdObservation);

  } catch (error) {
    console.error('FHIR API request failed:', error);
    // Handle API errors (e.g., resource not found, server error, authentication issue after retries)
    if (error instanceof SmartClientRequestError) {
        console.error("Details:", error.details);
    }
  }
}

// Main execution
initializeSmartClient()
  .then(client => {
    if (client) {
      return fetchPatientData(client, 'some-patient-id');
    }
  })
  .catch(err => {
    // Global error handling if needed
  });

Error Handling

The client throws specific error types:

  • SmartClientInitializationError: For issues during new SmartClient() or SmartClient.init().
  • SmartClientAuthenticationError: For problems related to JWT creation or obtaining an access token.
  • SmartClientRequestError: For failures during FHIR API requests (after successful token acquisition, though can include auth errors on retry).

These errors extend the base SmartClientError and may contain a cause (the original error) and details (additional context, like server response).

Project Structure

smart-client/
├── dist/                     # Compiled output (JavaScript and type definitions)
├── src/                      # TypeScript source files
│   ├── client.ts             # Core SmartClient class implementation
│   ├── types.ts              # TypeScript interfaces and error classes
│   ├── index.ts              # Main entry point, exports public API
│   └── client.test.ts        # Unit tests for the client
├── .gitignore                # Git ignore file
├── eslint-config.js          # ESLint configuration
├── LICENSE                   # License file
├── package.json              # Package manifest, dependencies, and scripts
├── README.md                 # This file
└── tsconfig.json             # TypeScript compiler options
└── vitest.config.ts          # Vitest configuration

Dependencies

  • axios: Promise-based HTTP client for Node.js and the browser.
    • Version: ^1.10.0
  • jose: A zero-dependency JWS, JWE, JWT, JWK, JWKS library for Node.js, Browser, Cloudflare Workers, Deno.
    • Version: ^5.6.3
  • uuid: For the creation of RFC4122 UUIDs.
    • Version: ^10.0.0

Dev Dependencies include standard monorepo tools like ESLint, TypeScript, and Vitest for testing.

How to Contribute

Contributions are welcome! Please follow these guidelines:

  1. Bug Reports: If you find a bug, please open an issue on the project's issue tracker. Include a clear description, steps to reproduce, and expected behavior.
  2. Feature Requests: Open an issue to discuss new features or improvements.
  3. Pull Requests:
    • Fork the repository and create a new branch for your feature or bug fix.
    • Ensure your code follows the existing coding style and conventions (ESLint should pass).
    • Write unit tests for any new functionality or bug fixes.
    • Make sure all tests pass (pnpm test within the package).
    • Update documentation (README, code comments) as necessary.
    • Open a pull request with a clear description of your changes.

License

This project is licensed under the MIT License. See the LICENSE file for more details.

Examples

See the Usage section for examples of initializing the client and making API requests.

Advanced: Using a JWK Private Key

If your private key is in JWK (JSON Web Key) format as a JSON string:

const config: SmartClientConfig = {
  // ... other config
  privateKey: JSON.stringify({
    kty: "RSA",
    kid: "my-rsa-key-2024",
    alg: "RS384",
    n: "verylongn...",
    e: "AQAB",
    d: "verylongd...",
    // ... other RSA private key components (p, q, dp, dq, qi)
  }),
  kid: "my-rsa-key-2024", // Ensure kid in config matches kid in JWK if both are present
  signingAlgorithm: 'RS384', // Ensure this matches the 'alg' in your JWK or is compatible
};