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

ehr-connect

v0.1.0

Published

Unified, Stripe-like developer experience for EHR and FHIR R4 integration

Readme


⚡ The Problem

Integrating with Electronic Health Record (EHR) systems like Epic, Oracle Health (Cerner), or Athena is notoriously difficult:

  • Fragmented Auth: SMART Backend Services requires RFC 7523 asymmetric JWT assertions (RS384 for Epic, RS256 for Oracle), JWKS hosting, and complex token caching.
  • Provider Quirks: Epic rejects Observation searches that lack an explicit category; Oracle requires custom endpoint prefixes; Cerner handles pagination links differently than HAPI FHIR.
  • Messy FHIR Schemas: FHIR R4 is deeply nested and flexible. Extracting a patient's name, primary phone, blood pressure, or active medication requires navigating dozens of optional arrays, codeableConcepts, and extensions.
  • Zero Unified Developer Experience: Developers have had to build custom OAuth flows, retry logic, secret redaction, and normalizers from scratch for every provider.

💡 The Solution: EHRConnect

ehr-connect gives developers a Stripe-like developer experience for healthcare data. One unified API connects to multiple EHR vendors with zero manual FHIR boilerplate.

npm install ehr-connect
import { createEHR } from "ehr-connect";

const ehr = createEHR({
  provider: "epic",
  environment: "sandbox",
  credentials: {
    clientId: process.env.EPIC_CLIENT_ID!,
    privateKey: process.env.EPIC_PRIVATE_KEY! // PKCS#8 PEM or JWK
  }
});

await ehr.connect();

// Normalized, clean TypeScript models across all EHRs
const patient = await ehr.patients.get("123");
console.log(patient.fullName, patient.gender, patient.birthDate);

const observations = await ehr.observations.list({ patientId: "123" });
const medications = await ehr.medications.list({ patientId: "123" });

🚀 Key Features

| Feature | Description | | :--- | :--- | | Unified 11 Domain Services | Standardized APIs for patients, practitioners, encounters, observations, conditions, medications, allergies, procedures, diagnosticReports, documents, and appointments. | | Normalized Data Layer | Deep FHIR resources mapped to clean, flat, strongly-typed TypeScript interfaces (fullName, primaryPhone, bloodPressure, status). Raw FHIR resource always preserved via raw. | | Zero-Config Mock Provider | Out-of-the-box in-memory mock EHR with 10 realistic, clinically correlated synthetic patients for instant local dev & unit tests without credentials or Docker. | | Production SMART-on-FHIR Engine | RFC 7523 Backend Services (private_key_jwt with RS384/RS256), Client Credentials, PKCE Authorization Code flow, token caching, and automatic renewal. | | EHR Quirk Abstraction | Automatically remedies provider-specific bugs, such as Epic's Observation category requirement and custom auth endpoint URLs. | | Raw FHIR Escape Hatch | Need vendor-specific extensions or raw FHIR operations? Use ehr.fhir.read(), search(), create(), patch(), transaction(), etc. directly. | | Async Streaming Iterators | for await (const obs of ehr.observations.iterate({ patientId })) streams through multi-page Bundle results with transparent cursor pagination. | | HIPAA & PHI Security | Built-in zero-password architecture, automatic redaction of secrets and JWTs in all logs and error objects, and exponential backoff retry with jitter. |


📦 Installation

# Using npm
npm install ehr-connect

# Using pnpm
pnpm add ehr-connect

# Using yarn
yarn add ehr-connect

🏃 5-Minute Quickstart (Zero-Config Mock)

Run immediately without needing any API keys or network access:

import { createEHR } from "ehr-connect";

async function run() {
  // 1. Initialize Mock Provider
  const ehr = createEHR({ provider: "mock" });
  await ehr.connect();

  // 2. Fetch all 10 synthetic patients
  const patients = await ehr.patients.list();
  console.log(`Loaded ${patients.length} patients.`);

  // 3. Inspect Patient #1 (John Michael Doe)
  const patient = await ehr.patients.get("1");
  console.log(`Patient: ${patient.fullName} (DOB: ${patient.birthDate})`);
  console.log(`Primary Phone: ${patient.primaryPhone}`);

  // 4. Retrieve Vital Signs
  const vitals = await ehr.observations.list({
    patientId: "1",
    category: "vital-signs"
  });
  console.log(`Vitals:`, vitals.map(v => `${v.codeText}: ${v.value ?? v.valueString} ${v.unit ?? ""}`));

  // 5. Clean up
  await ehr.disconnect();
}

run().catch(console.error);

🔌 Supported Providers

1. Epic Systems (provider: "epic")

Supports both the free open Epic Developer Sandbox and clinical production environments.

import { createEHR, generateRSAKeyPair } from "ehr-connect";

// Utility to generate RS384 keypair for Epic App registration
const keyPair = await generateRSAKeyPair(2048);
console.log("Paste this JWK into fhir.epic.com:", keyPair.jwks);

const ehr = createEHR({
  provider: "epic",
  environment: "sandbox", // Defaults to https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4
  credentials: {
    clientId: process.env.EPIC_CLIENT_ID!,
    privateKey: process.env.EPIC_PRIVATE_KEY! // PKCS#8 PEM string
  }
});

await ehr.connect();
const patient = await ehr.patients.get("erXuFYUfucBZ3GRGg1m0sw3"); // Epic Sandbox Test Patient

2. Oracle Health / Cerner (provider: "oracle")

Supports Oracle Health Millennium Sandbox (code.cerner.com) and Production.

const ehr = createEHR({
  provider: "oracle",
  environment: "sandbox",
  credentials: {
    clientId: process.env.ORACLE_CLIENT_ID!,
    clientSecret: process.env.ORACLE_CLIENT_SECRET // Or SMART private_key_jwt
  }
});

await ehr.connect();
const encounters = await ehr.encounters.list({ patientId: "12724066" });

3. Generic FHIR R4 (provider: "fhir")

Connect to any FHIR R4 server (e.g. HAPI FHIR, Google Cloud Healthcare API, Azure Health Data Services, AWS HealthLake, Smile CDR) with optional SMART discovery:

const ehr = createEHR({
  provider: "fhir",
  fhirBaseUrl: "http://localhost:8080/fhir",
  // Optional credentials:
  // credentials: { accessToken: "..." }
  // credentials: { clientId: "...", clientSecret: "..." }
  // credentials: { clientId: "...", privateKey: "..." }
});

await ehr.connect();

📚 Unified Domain Services API Reference

All services support strongly-typed search parameters, pagination, and return normalized models with the raw FHIR resource attached under .raw.

| Service | Methods | Description | | :--- | :--- | :--- | | ehr.patients | get(id), list(params), search(params) | Demographics, identifiers, contact info, address | | ehr.observations | get(id), list(params), search(params), iterate(params) | Vital signs, laboratory results, survey answers | | ehr.encounters | get(id), list(params), search(params), iterate(params) | Inpatient, outpatient, and ambulatory clinical visits | | ehr.conditions | get(id), list(params), search(params), iterate(params) | Problems, diagnoses, active health concerns | | ehr.medications | get(id), list(params), search(params), iterate(params) | Active prescriptions, orders, statements, dispense history | | ehr.allergies | get(id), list(params), search(params), iterate(params) | Drug, food, and environmental allergies & intolerances | | ehr.procedures | get(id), list(params), search(params), iterate(params) | Surgical operations, interventions, and diagnostics | | ehr.diagnosticReports | get(id), list(params), search(params), iterate(params) | Radiology reports, pathology summaries, lab panels | | ehr.documents | get(id), list(params), search(params), iterate(params) | Clinical notes, C-CDA summaries, discharge summaries | | ehr.appointments | get(id), list(params), search(params), iterate(params) | Scheduled and upcoming patient visits | | ehr.practitioners | get(id), list(params), search(params) | Clinicians, physicians, nurses, care team members |

Async Streaming Iterators

Avoid downloading thousands of items into memory at once. Use .iterate():

for await (const observation of ehr.observations.iterate({ patientId: "123", category: "laboratory" })) {
  console.log(observation.codeText, observation.value, observation.unit);
}

🛠 Raw FHIR Client Escape Hatch

When you need direct access to raw FHIR resources, batch operations, or vendor extensions:

// Read raw FHIR Patient
const rawPatient = await ehr.fhir.read("Patient", "123");

// Custom FHIR search
const bundle = await ehr.fhir.search("Observation", {
  subject: "Patient/123",
  code: "8867-4", // Heart rate
  _sort: "-date",
  _count: 10
});

// Create new resource
const created = await ehr.fhir.create("Encounter", {
  resourceType: "Encounter",
  status: "in-progress",
  class: { code: "AMB" },
  subject: { reference: "Patient/123" }
});

// JSON Patch
await ehr.fhir.patch("Patient", "123", [
  { op: "replace", path: "/gender", value: "female" }
]);

// Server Capabilities
const capabilities = await ehr.fhir.capabilities();

🔒 Security, HIPAA & Secret Redaction

EHRConnect is designed from the ground up for strict healthcare security guidelines:

  1. Zero EHR User Passwords: The library will NEVER prompt for or accept a physician or patient's EHR username/password. All interactions occur via SMART Backend Services (RFC 7523) or OAuth2 PKCE.
  2. Automatic Secret Redaction: All private keys, client secrets, access tokens, refresh tokens, auth codes, and Bearer authorization headers are automatically masked in loggers, error objects, and serialization ([REDACTED], Bearer [REDACTED_TOKEN]).
  3. Transient Memory Footprint: In-memory token stores purge sensitive key material on process termination and support encryption decorators.
  4. Resilience & Rate Limiting: Exponential backoff with full jitter handles HTTP 429 rate limits, HTTP 503 outages, and parses standard Retry-After headers.

🐳 Local Development with Docker HAPI FHIR

Run a local, unauthenticated open-source HAPI FHIR R4 server with pre-seeded test data in seconds:

# Start HAPI FHIR R4 container
docker compose -f docker/docker-compose.yml up -d

# Seed synthetic patient dataset
pnpm --filter=@ehr-connect/core exec tsx docker/seed-data.ts

🧪 Testing & Quality Assurance

EHRConnect has an extensive test suite with 104 tests and 86%+ code coverage:

# Run all unit and integration tests
pnpm test

# Generate coverage report
pnpm test:coverage

# Run TypeScript typechecks
pnpm typecheck

# Lint codebase
pnpm lint

📄 License

This library is open-source software licensed under the Apache License 2.0.