DL
Back to Blog
UncategorizedFebruary 3, 2026·5 min read

HAPI FHIR Server Setup and Configuration

How FhirHub configures and runs HAPI FHIR as its clinical data store in Docker with PostgreSQL, covering server settings, .NET API integration, and production considerations.

D

David Le

HAPI FHIR Server Setup and Configuration

By David Le -- Part 13 of the FhirHub Series

HAPI FHIR is the open-source reference implementation of the FHIR standard. FhirHub uses it as the clinical data store, running in Docker with a PostgreSQL backend. This final post covers how HAPI is configured, how the .NET API communicates with it, and what you need to know about running HAPI in production.

Why HAPI FHIR?

Several FHIR servers exist, but HAPI is the most widely adopted:

  • Open source (Apache 2.0 license)
  • Full R4 support with all resource types
  • Built-in REST API following the FHIR specification
  • JPA backend with PostgreSQL, MySQL, or H2
  • Active community and regular releases
  • Docker image available on Docker Hub

FhirHub uses HAPI as a data store -- it doesn't implement FHIR itself. The .NET API handles all business logic and delegates storage to HAPI.

Docker Configuration

hapi-fhir:
  image: hapiproject/hapi:latest
  container_name: fhirhub-hapi
  ports:
    - "8080:8080"
  environment:
    # Database
    - spring.datasource.url=jdbc:postgresql://postgres:5432/hapi
    - spring.datasource.username=hapi
    - spring.datasource.password=hapi
    - spring.datasource.driverClassName=org.postgresql.Driver
    - spring.jpa.properties.hibernate.dialect=ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect

    # FHIR settings
    - hapi.fhir.default_encoding=json
    - hapi.fhir.enable_repository_validating_interceptor=false
    - hapi.fhir.fhir_version=R4
    - hapi.fhir.allow_external_references=true
    - hapi.fhir.cors.allow_credentials=true
    - hapi.fhir.cors.allowed_origin=*

Database Configuration

HAPI uses Spring Data JPA with Hibernate. The key settings:

  • spring.datasource.url -- Points to the PostgreSQL container using Docker DNS (postgres:5432)
  • HapiFhirPostgresDialect -- HAPI's custom Hibernate dialect optimized for FHIR queries on PostgreSQL
  • PostgreSQL driver -- The standard JDBC PostgreSQL driver

FHIR Settings

SettingValuePurpose
default_encodingjsonReturn JSON instead of XML
enable_repository_validating_interceptorfalseSkip validation for dev performance
fhir_versionR4Use FHIR R4 (4.0.1)
allow_external_referencestrueAllow cross-server resource references
cors.allow_credentialstrueSupport authenticated CORS
cors.allowed_origin*Allow all origins (dev only)

Health Check

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8080/fhir/metadata"]
  interval: 30s
  timeout: 10s
  retries: 5
  start_period: 60s

The health check hits the FHIR CapabilityStatement endpoint. This is the standard FHIR discovery endpoint that every conformant server must support. If it returns 200, the server is operational.

The 60-second start_period is important -- HAPI runs database migrations on first boot, which can take time with PostgreSQL.

The Hl7.Fhir.R4 Client Library

The .NET API communicates with HAPI using the official Hl7.Fhir.R4 NuGet package (version 6.0.2):

<!-- FhirHubServer.Api.csproj -->
<PackageReference Include="Hl7.Fhir.R4" Version="6.0.2" />

This library provides:

  • Typed FHIR resources -- Patient, Observation, Condition, etc. as C# classes
  • FhirClient -- HTTP client for FHIR REST operations (CRUD, search, batch)
  • Serialization -- JSON/XML serialization with FHIR-aware handling
  • Search parameters -- Typed search parameter builders

FhirClient Factory

FhirHub uses a factory pattern for creating FHIR clients:

// Registered as singleton
builder.Services.AddSingleton<IFhirClientFactory, FhirClientFactory>();

The factory creates FhirClient instances configured with the HAPI FHIR base URL from configuration:

builder.Services.Configure<HapiFhirOptions>(
    builder.Configuration.GetSection("HapiFhir"));

With the base URL set to http://hapi-fhir:8080/fhir inside Docker.

Repository Pattern

Three repositories abstract HAPI FHIR operations:

builder.Services.AddScoped<IPatientRepository, HapiFhirPatientRepository>();
builder.Services.AddScoped<IDashboardRepository, HapiFhirDashboardRepository>();
builder.Services.AddScoped<IExportRepository, HapiFhirExportRepository>();

HapiFhirPatientRepository

Handles all patient-related FHIR operations:

  • Search patients -- GET /fhir/Patient?name=...
  • Get patient by ID -- GET /fhir/Patient/{id}
  • Create patient -- POST /fhir/Patient
  • Get vitals -- GET /fhir/Observation?patient={id}&category=vital-signs
  • Get conditions -- GET /fhir/Condition?patient={id}
  • Get medications -- GET /fhir/MedicationRequest?patient={id}
  • Get labs -- GET /fhir/Observation?patient={id}&category=laboratory
  • Get timeline -- Multiple queries aggregated chronologically

HapiFhirDashboardRepository

Aggregates data for the dashboard:

  • Patient counts
  • Observation statistics
  • Recent activity across all patients

HapiFhirExportRepository

Manages bulk data export jobs, leveraging HAPI's built-in export capabilities.

FHIR REST API

HAPI exposes the standard FHIR REST API:

OperationHTTP MethodURL Pattern
ReadGET/fhir/{type}/{id}
SearchGET/fhir/{type}?{params}
CreatePOST/fhir/{type}
UpdatePUT/fhir/{type}/{id}
DeleteDELETE/fhir/{type}/{id}
CapabilityStatementGET/fhir/metadata
Batch/TransactionPOST/fhir (Bundle)

Search Parameters

FHIR defines rich search capabilities. Common searches in FhirHub:

# Search patients by name
GET /fhir/Patient?name=smith

# Get vital signs for a patient
GET /fhir/Observation?patient=123&category=vital-signs

# Get active conditions
GET /fhir/Condition?patient=123&clinical-status=active

# Get active medication requests
GET /fhir/MedicationRequest?patient=123&status=active

Data Model

HAPI stores all FHIR resources in PostgreSQL. The database schema is auto-generated by Hibernate and includes tables for:

  • Resource content (JSON blob)
  • Search indexes (for efficient querying)
  • Tags and security labels
  • Version history (every update creates a new version)

FHIR's versioning means every resource has a version ID. When you update a Patient, the old version is preserved. This is important for clinical data where audit trails are required.

CapabilityStatement

The /fhir/metadata endpoint returns the server's CapabilityStatement -- a FHIR resource that describes what the server can do:

curl http://localhost:8080/fhir/metadata | jq '.fhirVersion'
# "4.0.1"

This is also used as the health check endpoint because it exercises the full stack (HTTP server, database connection, FHIR engine).

Production Recommendations

For production HAPI FHIR deployment:

  1. Enable validation -- Set enable_repository_validating_interceptor=true
  2. Restrict CORS -- Replace * with specific origins
  3. Add authentication -- HAPI supports OAuth2 interceptors
  4. Tune JVM memory -- Default heap may be too small for large datasets
  5. Configure indexing -- Custom search parameters for your use cases
  6. Enable audit logging -- HAPI can log all operations as AuditEvent resources
  7. Set up backups -- Regular PostgreSQL backups with point-in-time recovery
  8. Monitor performance -- HAPI exposes metrics via Spring Actuator

What's Next

In Part 14, we'll begin a 5-part DevOps sub-series starting with Docker multi-stage builds and a three-file Compose strategy that cleanly separates dev from production configuration.


Find the source code on GitHub Connect on LinkedIn

Related Projects

Featured

FhirHub

A healthcare data management platform built on the HL7 FHIR R4 standard, providing a comprehensive web interface for managing patient clinical data including vitals, conditions, medications, lab orders, and bulk data exports with role-based access control and full audit logging.

Next.js 16
React 19
Typescript
Tailwind CSS 4
+8