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
| Setting | Value | Purpose |
|---|---|---|
default_encoding | json | Return JSON instead of XML |
enable_repository_validating_interceptor | false | Skip validation for dev performance |
fhir_version | R4 | Use FHIR R4 (4.0.1) |
allow_external_references | true | Allow cross-server resource references |
cors.allow_credentials | true | Support 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:
| Operation | HTTP Method | URL Pattern |
|---|---|---|
| Read | GET | /fhir/{type}/{id} |
| Search | GET | /fhir/{type}?{params} |
| Create | POST | /fhir/{type} |
| Update | PUT | /fhir/{type}/{id} |
| Delete | DELETE | /fhir/{type}/{id} |
| CapabilityStatement | GET | /fhir/metadata |
| Batch/Transaction | POST | /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:
- Enable validation -- Set
enable_repository_validating_interceptor=true - Restrict CORS -- Replace
*with specific origins - Add authentication -- HAPI supports OAuth2 interceptors
- Tune JVM memory -- Default heap may be too small for large datasets
- Configure indexing -- Custom search parameters for your use cases
- Enable audit logging -- HAPI can log all operations as AuditEvent resources
- Set up backups -- Regular PostgreSQL backups with point-in-time recovery
- 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.