helios-hfs 0.2.0

Helios FHIR Server - A high-performance FHIR R4/R4B/R5/R6 server
helios-hfs-0.2.0 is not a library.

Helios FHIR Server (HFS)

A high-performance FHIR server built in Rust.

An open test server is available at https://hfs.heliossoftware.com/ for experimentation and evaluation.

Features

  • Full FHIR RESTful API support
  • Multiple FHIR version support
  • Pluggable storage backends (SQLite, PostgreSQL, MongoDB)
  • Content negotiation (JSON)
  • Conditional operations with ETag support
  • Multi-tenant support via X-Tenant-ID header
  • CORS support

Installation

From Source

# Clone the repository
git clone https://github.com/HeliosSoftware/hfs.git
cd hfs

# Build with default features (R4 + SQLite)
cargo build --release -p helios-hfs

# Build with all FHIR versions
cargo build --release -p helios-hfs --features R4,R4B,R5,R6,sqlite

Usage

Running the Server

# Run with default settings (R4, SQLite, port 8080)
./target/release/hfs

# Specify a different port
./target/release/hfs --port 3000

# Use an in-memory database
./target/release/hfs --database-url :memory:

# Enable debug logging
./target/release/hfs --log-level debug

Command Line Options

Usage: hfs [OPTIONS]

Options:
      --port <PORT>              Server port [env: HFS_SERVER_PORT=] [default: 8080]
      --host <HOST>              Host to bind [env: HFS_SERVER_HOST=] [default: 127.0.0.1]
      --log-level <LOG_LEVEL>    Log level (error, warn, info, debug, trace)
                                 [env: HFS_LOG_LEVEL=] [default: info]
      --database-url <URL>       Database connection URL [env: DATABASE_URL=]
      --data-dir <PATH>          Path to FHIR data directory containing search parameter
                                 definitions [env: HFS_DATA_DIR=] [default: ./data]
      --max-body-size <BYTES>    Maximum request body size [env: HFS_MAX_BODY_SIZE=]
                                 [default: 10485760]
      --request-timeout <SECS>   Request timeout in seconds [env: HFS_REQUEST_TIMEOUT=]
                                 [default: 30]
      --enable-cors              Enable CORS [env: HFS_ENABLE_CORS=] [default: true]
      --cors-origins <ORIGINS>   Allowed CORS origins [env: HFS_CORS_ORIGINS=] [default: *]
  -h, --help                     Print help
  -V, --version                  Print version

Configuration

Environment Variables

Variable Default Description
HFS_SERVER_PORT 8080 Server port
HFS_SERVER_HOST 127.0.0.1 Host to bind
HFS_LOG_LEVEL info Log level (error, warn, info, debug, trace)
DATABASE_URL fhir.db Database connection string
HFS_DATA_DIR ./data Path to FHIR data directory (search parameters)
HFS_MAX_BODY_SIZE 10485760 Max request body size (bytes; applies to the decompressed body for compressed requests)
HFS_REQUEST_TIMEOUT 30 Request timeout (seconds)
HFS_ENABLE_CORS true Enable CORS
HFS_CORS_ORIGINS * Allowed CORS origins
HFS_CORS_METHODS GET,POST,PUT,DELETE,OPTIONS Allowed HTTP methods
HFS_CORS_HEADERS Content-Type,Authorization,X-Requested-With Allowed headers
HFS_DEFAULT_TENANT default Default tenant ID
HFS_TERMINOLOGY_SERVER (none) Terminology server URL for :in/:not-in modifiers and FHIRPath memberOf()/subsumes()
HFS_COMPOSITE_SYNC_MODE asynchronous Composite-store write sync mode for ES-backed backends (sqlite-elasticsearch, postgres-elasticsearch, mongodb-elasticsearch, s3-elasticsearch). One of asynchronous, synchronous, hybrid. With asynchronous (default) the write returns as soon as the primary commits and the search backend is updated on a background worker — lowest latency, but a follow-up search can race the indexing. Use synchronous when callers need read-your-write semantics (e.g. integration tests, bulk-load flows that immediately search). Ignored when the storage backend has no search secondary.

FHIR Version Support

Build with specific FHIR versions using feature flags:

# R4 only (default)
cargo build -p helios-hfs --features R4,sqlite

# R5 only
cargo build -p helios-hfs --no-default-features --features R5,sqlite

# Multiple versions
cargo build -p helios-hfs --features R4,R4B,R5,R6,sqlite

Database Backends

SQLite (Default)

cargo build -p helios-hfs --features sqlite

# Run with file-based database
./target/release/hfs --database-url ./data/fhir.db

# Run with in-memory database
./target/release/hfs --database-url :memory:

PostgreSQL

cargo build -p helios-hfs --no-default-features --features R4,postgres

./target/release/hfs --database-url "postgresql://user:pass@localhost/fhir"

MongoDB

cargo build -p helios-hfs --no-default-features --features R4,mongodb

./target/release/hfs --database-url "mongodb://localhost:27017/fhir"

API Endpoints

Interaction Method URL
capabilities GET /metadata
read GET /[type]/[id]
vread GET /[type]/[id]/_history/[vid]
update PUT /[type]/[id]
patch PATCH /[type]/[id]
delete DELETE /[type]/[id]
create POST /[type]
search GET/POST /[type]?params or /[type]/_search
history (instance) GET /[type]/[id]/_history
history (type) GET /[type]/_history
history (system) GET /_history
batch/transaction POST /
health GET /health

Examples

Create a Patient

curl -X POST http://localhost:8080/Patient \
  -H "Content-Type: application/fhir+json" \
  -d '{
    "resourceType": "Patient",
    "name": [{"family": "Smith", "given": ["John"]}],
    "birthDate": "1970-01-01"
  }'

Read a Patient

curl http://localhost:8080/Patient/123

Search for Patients

curl "http://localhost:8080/Patient?family=Smith"

Get CapabilityStatement

curl http://localhost:8080/metadata

Batch and Transaction Support

HFS supports both batch and transaction bundles for processing multiple operations in a single request.

Note: Transaction bundles require ACID transaction support. The default SQLite backend fully supports transactions. If using other backends, check the capability matrix in the persistence crate documentation.

Transaction Bundle (Atomic)

All operations succeed or all fail. Entries are processed in FHIR-specified order: DELETE → POST → PUT → GET.

curl -X POST http://localhost:8080/ \
  -H "Content-Type: application/fhir+json" \
  -d '{
    "resourceType": "Bundle",
    "type": "transaction",
    "entry": [
      {
        "fullUrl": "urn:uuid:patient-1",
        "resource": {
          "resourceType": "Patient",
          "name": [{"family": "Smith"}]
        },
        "request": {
          "method": "POST",
          "url": "Patient"
        }
      },
      {
        "resource": {
          "resourceType": "Observation",
          "subject": {"reference": "urn:uuid:patient-1"},
          "code": {"text": "Blood Pressure"}
        },
        "request": {
          "method": "POST",
          "url": "Observation"
        }
      }
    ]
  }'

The urn:uuid:patient-1 reference is automatically resolved to the actual Patient ID after creation.

Batch Bundle (Independent)

Each operation is processed independently; failures don't affect other entries.

curl -X POST http://localhost:8080/ \
  -H "Content-Type: application/fhir+json" \
  -d '{
    "resourceType": "Bundle",
    "type": "batch",
    "entry": [
      {
        "request": {
          "method": "GET",
          "url": "Patient/123"
        }
      },
      {
        "request": {
          "method": "DELETE",
          "url": "Patient/456"
        }
      }
    ]
  }'

Current Limitations

The following FHIR bundle features are not yet implemented:

  • Conditional reference resolution (Patient?identifier=12345)
  • PATCH method in bundles
  • Prefer header handling (return=minimal, etc.)

Search Parameter Configuration

HFS loads FHIR SearchParameter definitions from JSON bundle files to enable comprehensive search functionality. By default, these files are expected in a data/ directory relative to the working directory or executable.

Data Directory Structure

data/
├── search-parameters-r4.json   # FHIR R4 SearchParameters (HL7 spec)
├── search-parameters-r4b.json  # FHIR R4B SearchParameters (HL7 spec)
├── search-parameters-r5.json   # FHIR R5 SearchParameters (HL7 spec)
├── search-parameters-r6.json   # FHIR R6 SearchParameters (auto-downloaded at build time)
└── *.json                      # Custom SearchParameter files (see below)

Search Parameter Loading

On startup, HFS loads SearchParameters in this order:

  1. Minimal fallback - Built-in _id, _lastUpdated, _tag, _profile, _security (always available)
  2. Spec file - Loads from the appropriate search-parameters-*.json based on configured FHIR version
  3. Custom files - Loads any additional .json files in the data directory (not matching search-parameters-*.json)
  4. Stored parameters - Loads any custom SearchParameters POSTed to the server

Custom SearchParameter Files

You can add custom SearchParameters by placing JSON files in the data directory. Each file can contain:

  • A single SearchParameter resource
  • An array of SearchParameter resources
  • A FHIR Bundle containing SearchParameter resources

Example custom SearchParameter file (data/custom-search-params.json):

{
  "resourceType": "SearchParameter",
  "id": "patient-mrn",
  "url": "http://example.org/fhir/SearchParameter/patient-mrn",
  "name": "mrn",
  "status": "active",
  "code": "mrn",
  "base": ["Patient"],
  "type": "token",
  "expression": "Patient.identifier.where(type.coding.code='MR')"
}

Or as a Bundle:

{
  "resourceType": "Bundle",
  "type": "collection",
  "entry": [
    {
      "resource": {
        "resourceType": "SearchParameter",
        "url": "http://example.org/fhir/SearchParameter/patient-mrn",
        ...
      }
    }
  ]
}

Custom Data Directory

Specify a custom location for the data files:

# Via command line
./target/release/hfs --data-dir /opt/hfs/data

# Via environment variable
HFS_DATA_DIR=/opt/hfs/data ./target/release/hfs

If the spec file is missing, HFS logs a warning and continues with minimal fallback parameters. This ensures the server can start even without the full spec files, though search functionality will be limited.

R6 Automatic Download

When building with the R6 feature enabled, the search-parameters-r6.json file is automatically downloaded from the HL7 build server during compilation. The download is skipped if:

  • The file already exists and is less than 24 hours old
  • The DOCS_RS environment variable is set (for docs.rs builds)

Multi-Tenant Support

Use the X-Tenant-ID header to isolate data between tenants:

curl -H "X-Tenant-ID: clinic-a" http://localhost:8080/Patient
curl -H "X-Tenant-ID: clinic-b" http://localhost:8080/Patient