Skip to content

Migration from HAPI FHIR

This guide helps you migrate from HAPI FHIR JPA Server to OctoFHIR.

FeatureHAPI FHIROctoFHIR
FHIR VersionsR4, R4B, R5R4, R4B, R5, R6
StorageJPA (SQL)PostgreSQL (native)
LanguageJavaRust
Memory (idle)2-4 GB~200 MB
Cold Start30-60s<5s
SMART on FHIRVia interceptorsBuilt-in
GraphQLPluginBuilt-in
Bulk ExportYesYes
SubscriptionsYesPlanned
TerminologyFull TX serverExternal TX client

MetricHAPI FHIROctoFHIR
Read p95 latency50-100ms<20ms
Write TPS82-3,000500+
Search latency100-500ms20-100ms
Memory (load)4-8 GB500 MB

Use HAPI’s Bulk Export operation:

Terminal window
# Initiate system-level export
curl -X GET "https://hapi-fhir.example.com/fhir/$export" \
-H "Accept: application/fhir+json" \
-H "Prefer: respond-async"

Or export specific resource types:

Terminal window
curl -X GET "https://hapi-fhir.example.com/fhir/$export?_type=Patient,Observation,Condition" \
-H "Accept: application/fhir+json" \
-H "Prefer: respond-async"

OctoFHIR accepts standard FHIR resources. However, check for:

  • Custom extensions - Ensure they’re valid FHIR
  • Local code systems - May need mapping
  • Resource references - Must use standard format

Use transaction bundles for bulk import:

Terminal window
# Split NDJSON into bundles of 1000 resources
split -l 1000 patients.ndjson patient_batch_
# Import each batch
for file in patient_batch_*; do
curl -X POST "https://octofhir.example.com/fhir" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/fhir+json" \
-d @"$file"
done

application.yaml
hapi:
fhir:
server:
path: /fhir
fhir_version: R4
default_page_size: 20
max_page_size: 200
spring:
datasource:
url: jdbc:postgresql://localhost:5432/hapi
username: hapi
password: secret
// Custom interceptor
@Interceptor
public class AuthInterceptor {
@Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED)
public void authorize(RequestDetails details) {
// Custom auth logic
}
}

HAPI FHIROctoFHIR
/fhir (configurable)/fhir
HAPI FHIROctoFHIR
Custom interceptorsBuilt-in OAuth 2.0
Header-based (custom)Authorization: Bearer <token>
EndpointHAPI FHIROctoFHIR
CapabilityStatement/fhir/metadata/fhir/metadata
CRUD/fhir/{Type}/{id}/fhir/{Type}/{id}
Search/fhir/{Type}?params/fhir/{Type}?params
TransactionPOST /fhirPOST /fhir
Bulk Export/$export/$export
GraphQL/fhir/$graphql/$graphql

Most search parameters work identically. Key differences:

ModifierHAPI FHIROctoFHIR
:exactYesYes
:containsYesYes
:missingYesYes
:notYesYes
:textYesPlanned
:above (token)YesPlanned
:below (token)YesPlanned

Both support chained searches with identical syntax:

GET /Observation?subject:Patient.name=John
GET /Patient?_has:Observation:subject:code=1234-5

Features in HAPI FHIR not yet available in OctoFHIR:

FeatureStatusETA
SubscriptionsPlannedQ2 2024
$validate operationPartialQ1 2024
Terminology serverExternal only-
MDM (Master Data Management)Not planned-
Consent managementPlannedQ3 2024

HAPI FHIR allows numeric IDs by default. OctoFHIR uses UUIDs:

GET /Patient/123
GET /Patient/550e8400-e29b-41d4-a716-446655440000

Both servers handle date precision, but behavior may differ slightly:

Terminal window
# Search for birthdate in 1990 (any day)
GET /Patient?birthdate=1990
# Search for exact date
GET /Patient?birthdate=1990-01-15

Both process transaction bundle entries in order, resolving references. Ensure dependent resources come after their dependencies.

Both return OperationOutcome, but messages may differ:

{
"resourceType": "OperationOutcome",
"issue": [{
"severity": "error",
"code": "invalid",
"diagnostics": "Resource validation failed"
}]
}

If you encounter migration issues:

Include:

  • HAPI FHIR version
  • Sample failing request/response
  • Expected vs actual behavior