Developer guide

Convert HL7 v2 to FHIR in Python

Bridge HL7 v2 feeds to a FHIR server with MessageFoundry: receive HL7 over MLLP, map the fields you need to a FHIR resource in a code-first Python handler, and deliver it over FHIR REST. The mapping is ordinary Python — reviewable, testable, and yours, never buried in a connector.

MessageFoundry ships a first-class FHIR destination. Your handler builds a FHIR-JSON resource (or a transaction Bundle) and the connection delivers it to your server's base URL as application/fhir+json — defaulting to R4B (R5 and STU3 are selectable).

1. Install the engine

Install MessageFoundry from PyPI. The optional [fhir] extra adds typed validation against the FHIR spec before delivery.

terminal
pip install messagefoundry
# optional — typed FHIR validation before delivery
pip install "messagefoundry[fhir]"

2. Declare an HL7 inbound and a FHIR outbound

Receive HL7 v2 over MLLP, and declare a FHIR REST destination pointed at your server's base URL (e.g. https://host/fhir). Keep the URL and any token in env() so each environment resolves its own.

config/lab_to_fhir.py
import json
from messagefoundry import MLLP, FHIR, Send, env, inbound, outbound, router, handler

# receive HL7 v2 lab results over MLLP
inbound("IB_Lab_ORU", MLLP(port=2575), router="oru_router")

# deliver FHIR resources to your FHIR server (create = POST {base}/{ResourceType})
outbound("OB_FHIR", FHIR(url=env("fhir_base_url"), interaction="create"))

3. Map HL7 fields to a FHIR resource

The mapping lives in your handler, as plain Python. Read HL7 fields by path and build the resource you want, then Send the JSON to the FHIR outbound.

config/lab_to_fhir.py
@router("oru_router")
def route(msg):
    if msg["MSH-9.1"] != "ORU":
        return []                       # UNROUTED
    return ["to_fhir"]

@handler("to_fhir")
def to_fhir(msg):
    # code-first mapping: HL7 v2 PID → FHIR Patient
    patient = {
        "resourceType": "Patient",
        "identifier": [{"value": msg["PID-3"]}],
        "name": [{"family": msg["PID-5.1"], "given": [msg["PID-5.2"]]}],
        "birthDate": msg["PID-7"],
        "gender": {"M": "male", "F": "female"}.get(msg["PID-8"], "unknown"),
    }
    return Send("OB_FHIR", json.dumps(patient))

Each HL7 v2 segment maps to the FHIR resource that fits it:

HL7 v2FHIR
PID-3Patient.identifier.value
PID-5.1 / PID-5.2Patient.name.family / .given
PID-7 / PID-8Patient.birthDate / .gender
OBR / OBXObservation (one per result), referencing the Patient

4. Run the engine

Point MessageFoundry at your config and environment. env("fhir_base_url") resolves from environments/<env>.toml; secrets like a bearer token resolve from the environment, never the config file.

terminal
python -m messagefoundry serve --config config --env dev --db messagefoundry.db

Idempotency, built in. FHIR delivery is at-least-once, so the server operation should be idempotent. The connection's native levers cover it: interaction="update" (PUT by id), or a conditional knob — if-none-exist, conditional-update, or if-match.