Dashboard

Storage Adapters

The SHL SDK supports multiple storage backends. Choose based on your infrastructure:

Comparison

AdapterUse CaseInfra RequiredPeer DependencyCost
FhirflyStorageZero-infra, just worksNoneNoneIncluded free
LocalStorageDevelopment, testingFilesystemNone
S3StorageAWS productionS3 bucket@aws-sdk/client-s3Your AWS bill
AzureStorageAzure productionBlob container@azure/storage-blobYour Azure bill
GCSStorageGCP productionGCS bucket@google-cloud/storageYour GCP bill

Store encrypted content on FHIRfly's hosted service. No infrastructure to manage. Included free in all plans.

import { SHL } from "@fhirfly-io/shl";

const storage = new SHL.FhirflyStorage({
  apiKey: process.env.FHIRFLY_API_KEY,
  // apiBaseUrl: "https://api.fhirfly.io", // default
});

FHIRfly stores only opaque encrypted blobs. The decryption key is embedded in the shlink:/ URL and never sent to FHIRfly. This means FHIRfly cannot decrypt your content.

Per-Plan Limits

PlanActive SHLsAttachments/SHLPer-file limit
Free25101 MB
Developer500101 MB
Pro5,000101 MB
Business50,000101 MB
EnterpriseUnlimited101 MB

"Active" means non-revoked and non-expired. Revoke unused SHLs to free up quota. See Pricing for full plan details.

LocalStorage

Filesystem storage for local development and testing.

const storage = new SHL.LocalStorage({
  directory: "./shl-data",
  baseUrl: "http://localhost:3456/shl",
});

S3Storage

AWS S3 storage for production workloads.

npm install @aws-sdk/client-s3
const storage = new SHL.S3Storage({
  bucket: "my-shl-bucket",
  region: "us-east-1",
  baseUrl: "https://shl.example.com",
  prefix: "shl-data/", // optional
});

AzureStorage

Azure Blob Storage for Azure-based deployments.

npm install @azure/storage-blob
const storage = new SHL.AzureStorage({
  container: "shl-data",
  connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
  baseUrl: "https://shl.example.com",
  prefix: "shl-data/", // optional
});

GCSStorage

Google Cloud Storage for GCP-based deployments.

npm install @google-cloud/storage
const storage = new SHL.GCSStorage({
  bucket: "my-shl-bucket",
  baseUrl: "https://shl.example.com",
  prefix: "shl-data/", // optional
});

Storage Interface

All storage adapters implement the SHLStorage interface:

interface SHLStorage {
  /** Base URL for manifest access */
  readonly baseUrl: string;

  /** Store a file (key = "{shlId}/{filename}") */
  store(key: string, content: string | Uint8Array): Promise<void>;

  /** Delete all files for an SHL (prefix = "{shlId}/") */
  delete(prefix: string): Promise<void>;
}

You can implement this interface to create custom storage adapters for any backend.

Concurrency Note

When self-hosting with cloud storage (S3, Azure, GCS), the SDK's access counter uses a plain read-modify-write pattern without conditional writes or ETags. Under concurrent load, a few extra accesses may be allowed beyond maxAccesses. For strict one-time links, use FhirflyStorage (which uses atomic MongoDB $inc) or add your own atomic counter (e.g., Redis, DynamoDB). See the Server Guide for details.