Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

FHIR connectors

Many systems around a CDR speak FHIR. EHRbase-rs ships a set of FHIR R4 connectors so it can take FHIR resources in, hand openEHR data back out as FHIR, and emit FHIR resources to downstream systems — all driven by mappings you control. It is not a full FHIR server; it is a focused, mapping-driven bridge between the FHIR and openEHR worlds.

The connectors come in two independent switches — an inbound/read-façade switch and an outbound-emission switch — because they have very different data-exposure characteristics. All FHIR routes are relative to the API base path (/ehrbase/rest/openehr/v1), use FHIR R4, and speak application/fhir+json. A resource type the connector does not map yet is answered with a FHIR OperationOutcome, never a silent success.

Inbound ingestion

POST /fhir/r4/{resource_type} takes a FHIR resource and stores it as a validated openEHR composition. The connector resolves the mapping for the resource type (and its meta.profile, if any), resolves or creates the EHR from the resource’s subject, builds a composition from the mapping, stamps it with a FEEDER_AUDIT recording the FHIR origin, and commits it through the normal validated write path. If the mapped composition fails validation, the request is rejected with 422 and nothing is stored; a successful ingest returns 201 with ETag and Location headers pointing at the openEHR composition. The starter set of supported resource types is Patient, Observation, Condition, and DocumentReference.

Read façade

GET /fhir/r4/{resource_type}?patient=<subject> returns openEHR data reverse-mapped into a FHIR searchset Bundle. The patient parameter is mandatory (a missing one is a 400) — this is a targeted façade, not a general FHIR search. An optional _count caps the number of entries. Each Bundle entry is a FHIR resource produced from a stored composition by running the mapping in reverse.

Outbound emission

Outbound emission publishes the mapped FHIR resource for every relevant commit — but the target is an AMQP broker (RabbitMQ), not an HTTP FHIR server. A background task drains the same commit outbox used by change events, reverse-maps each committed composition through every enabled mapping bound to its template, and publishes each resulting FHIR resource to a topic exchange (default ehrbase.fhir) with a routing key of <resource_type>.<template_id>. Delivery is at-least-once.

Warning

Outbound FHIR messages carry PHI — the payload is the mapped clinical FHIR resource itself, unlike the PHI-free change-event envelopes. That is exactly why they are a separate switch on a separate exchange (ehrbase.fhir, not ehrbase.events): broker access control can then isolate the PHI-bearing stream. Enable it only against a TLS, access-controlled broker, and treat every consumer as a PHI processor.

Mappings are data you manage

There are no bundled mapping files. Each mapping is a stored definition managed through an admin API (classed under admin authorization):

MethodPathPurpose
GET/admin/fhir_mappinglist mappings
POST/admin/fhir_mappingcreate a mapping (201)
GET/admin/fhir_mapping/{mapping_id}get a mapping
PUT/admin/fhir_mapping/{mapping_id}update a mapping
DELETE/admin/fhir_mapping/{mapping_id}delete a mapping (204)

A mapping definition binds one FHIR resource type (optionally scoped to a meta.profile URL) to one openEHR template, and lists field bindings — each mapping an openEHR FLAT path to a FHIR path (or a constant), shaped by a transform (plain text, date, quantity with unit, or a coded value with a code-system-to-terminology mapping). The FHIR-path support is a deliberate subset covering field navigation and array indexing (for example component[1].valueQuantity.value), and the mapping is symmetric: the same definition drives inbound ingest, the read façade, and outbound emission.

Note

The template a mapping references must already be ingested (see Templates & validation) — creating a mapping against an unknown template is a 400. Mapping names are immutable once set, and a duplicate name is a 409.

Enabling the connectors

Both switches are off by default. The inbound/read-façade switch lives in the REST config; the outbound emitter has its own config group:

Environment variableDefaultMeaning
EHRBASE_REST_FHIR__ENABLEDfalseenable inbound ingest, the read façade, and the mapping API
EHRBASE_FHIR_OUTBOUND_ENABLEDfalseenable outbound emission to AMQP
EHRBASE_FHIR_OUTBOUND_URLamqp://guest:guest@localhost:5672/%2foutbound broker URL
EHRBASE_FHIR_OUTBOUND_EXCHANGEehrbase.fhiroutbound topic exchange (kept distinct from the event stream)
EHRBASE_FHIR_OUTBOUND_TLSfalseupgrade an amqp:// URL to amqps://
EHRBASE_FHIR_OUTBOUND_BATCH_SIZE128commits drained per cycle
EHRBASE_FHIR_OUTBOUND_POLL_INTERVAL_MS1000poll interval while idle
EHRBASE_FHIR_OUTBOUND_PUBLISH_MAX_RETRIES3retries per message

When the inbound switch is off, the /fhir/r4/* and /admin/fhir_mapping routes answer 404 without touching the backend. When the outbound switch is off, no emitter task runs.