# FHIR connections

Give every service secure access to any FHIR server as self.fhir - credentials, pooling and guaranteed delivery included.

Python services reach FHIR servers through outgoing connections. You create a connection once in the Dashboard and every service can use it as `self.fhir` - the client it returns attaches the credentials, pools the network connections and converts search results to Python objects.

## Create a connection {#create-a-connection}

To create a connection, go to **Connections** > **Outgoing** > **HL7** > **FHIR** in the Dashboard, click **Create a new connection** and fill in the form:

1. **Name:** FHIR.Sample
2. **Address:** your FHIR server's address, e.g. `https://fhir.example.com`
3. **Security:** No security definition - servers that require credentials are covered in [FHIR security](https://zato.io/docs/dev/healthcare/hl7/fhir/security/index.html)
4. Click **OK**

[Video: New FHIR connection](https://zatosource-production.b-cdn.net/docs/gfx/outgoing/fhir-connection-create.webm?v=1791135940)

Dashboard menu: Connections > Outgoing > HL7 > FHIR

The connection is ready when you click OK - configuration changes propagate to all servers automatically, with no restarts.

You can also set **Pool size** in the same form - how many network connections Zato keeps open to the server, 10 by default. Requests above that number wait for a connection to free up rather than opening new ones.

## Use the connection in a service {#use-the-connection-in-a-service}

The `self.fhir` dictionary returns a client for the connection whose name you pass. The service below looks up active patients on the server, sorted by their birth date:

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class GetActivePatients(Service):
    """ Looks up active patients on a FHIR server, sorted by birth date.
    """
    name = 'demo.fhir.get-active-patients'

    def handle(self) -> 'None':

        # A client connected to the server behind FHIR.Sample ..
        client = self.fhir['FHIR.Sample']

        # .. everything about patients starts here ..
        patients = client.resources('Patient')

        # .. all of the active ones, sorted by their birth date ..
        result = patients.search(active=True).sort('-birthdate')

        # .. and each match goes to the server log.
        for patient in result:
            self.logger.info('Received %s', patient['name'])
```

Invoking the service writes the received data to the server log:

```text
INFO - Received [{'use': 'official', 'family': 'Chalmers', 'given': ['Peter', 'James']}]
```

To learn what else the client offers, see [resources](https://zato.io/docs/dev/healthcare/hl7/fhir/resources/index.html) for creating, reading and updating data, and [searches and bundles](https://zato.io/docs/dev/healthcare/hl7/fhir/bundles/index.html) for queries and pagination.

## Queue documents for guaranteed delivery {#queue-documents-for-guaranteed-delivery}

A call to `.save` sends the resource to the server immediately - if the server is down or slow, the call raises an error the service must handle. When the document only needs to reach the server eventually, and the service does not need the answer, publish it instead:

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class PublishPatient(Service):
    """ Queues a patient document for guaranteed delivery to a FHIR server.
    """
    name = 'demo.fhir.publish-patient'

    def handle(self) -> 'None':

        # The document to store, its resourceType saying what it is ..
        patient = {
            'resourceType': 'Patient',
            'name': [{'family': 'Chalmers', 'given': ['Peter', 'James']}],
        }

        # .. a client connected to the server behind FHIR.Sample ..
        client = self.fhir['FHIR.Sample']

        # .. and the publish call, returning as soon as the document is queued.
        result = client.publish(patient)

        self.logger.info('Queued as %s', result.msg_id)
```

The call returns in a few milliseconds and does not touch the network. Zato delivers the document in the background, retrying with a growing interval - from 3 seconds up to 10 seconds between attempts - for up to 30 days. The queue is stored in a database, so delivery resumes after a restart, and a document leaves the queue only once the server confirms that it stored it.

Each connection has a queue of its own and documents are delivered in the order they were published. Delivery uses the connection's own address, credentials and pool, the same ones a `.save` uses, and the document is created under the path its `resourceType` names - a Patient published to a connection addressed `https://fhir.example.com/r4` is posted to `https://fhir.example.com/r4/Patient`.

A document without a `resourceType` is rejected at publish time, because no delivery path can be derived for it.

After you rename a connection, the documents queued under the old name are delivered under the new one, without duplicates. Deleting a connection deletes its queue - the remaining documents are dropped and their count is written to the server log.

## Secure the connection {#secure-the-connection}

A server that requires credentials takes a [Basic Auth](https://zato.io/docs/security/basic-auth.html) or OAuth definition in the connection's **Security** field - Zato attaches the credentials to every request, and OAuth tokens are acquired and refreshed automatically. Servers behind TLS with non-public certificates take a CA bundle uploaded through the Dashboard. To set up each of these, see [FHIR security](https://zato.io/docs/dev/healthcare/hl7/fhir/security/index.html).

## Export a population {#export-a-population}

The form's **Bulk export** tab pulls a whole patient group, or the whole server, out through the FHIR `$export` operation on a schedule and delivers the NDJSON files to SFTP, Kafka, another FHIR server or a service of yours - the [bulk export](https://zato.io/docs/dev/healthcare/hl7/fhir/bulk-export/index.html) page covers each of its fields.

## Watch the connection {#watch-the-connection}

The form's **Alerts** tab defines how the connection is monitored.

A **health check** gives the connection a heartbeat. Under **Health check**, click the **Schedule** line and set **Ping every** to a number and its unit - Zato then reads the server's `CapabilityStatement` that often and records each read in the [audit log](https://zato.io/docs/admin/audit-log/index.html) under the `FHIR checks` source, so a server that stopped answering is noticed even on a connection nobody is calling. The [health checks](https://zato.io/docs/admin/health-checks.html) chapter has the details.

## Define connections in YAML {#define-connections-in-yaml}

Instead of filling out the Dashboard form, you can declare connections in YAML and import them with [enmasse](https://zato.io/docs/admin/enmasse.html), the import tool used by automated deployments and version-controlled configuration:

```yaml
outgoing_fhir:

  - name: FHIR.Sample
    address: https://fhir.example.com/r4

  - name: FHIR.Secured
    address: https://fhir.partner.example.com/r4
    security: my.basic_auth.1
    pool_size: 20

  - name: FHIR.Monitored
    address: https://fhir.example.com/r4
    health_check_run_every: 5
    health_check_run_unit: minutes
```

Every field is listed in the [enmasse reference](https://zato.io/docs/admin/enmasse-reference.html).

## See also {#see-also}

- [Resources](https://zato.io/docs/dev/healthcare/hl7/fhir/resources/index.html) - Create, read, update and delete any FHIR resource
- [Searches and bundles](https://zato.io/docs/dev/healthcare/hl7/fhir/bundles/index.html) - Search parameters, result bundles and pagination
- [Security](https://zato.io/docs/dev/healthcare/hl7/fhir/security/index.html) - Basic Auth, OAuth and TLS for FHIR servers
- [Bulk export](https://zato.io/docs/dev/healthcare/hl7/fhir/bulk-export/index.html) - Whole populations pulled out with $export and delivered as NDJSON files
- [AI agents](https://zato.io/docs/dev/healthcare/ai/agents.html) - Expose FHIR-backed services to AI agents as MCP tools
- [FHIR tutorial](https://zato.io/tutorials/healthcare/fhir.html) - A connection, services and searches built end to end

## Learn more {#learn-more}

- [Healthcare interface engine](https://zato.io/docs/dev/healthcare/) - Clinical messages, FHIR, conversion and operations
- [HL7 v2 parsing](https://zato.io/docs/dev/healthcare/hl7/v2/parsing/) - Messages as typed objects, with field access and validation
- [MLLP channels](https://zato.io/docs/dev/healthcare/hl7v2/mllp/) - Receiving and sending HL7 v2 over MLLP sockets
- [FHIR integrations](https://zato.io/docs/dev/healthcare/hl7/fhir/) - Read and write FHIR resources, with paths, bundles and extensions
- [HL7 v2 to FHIR](https://zato.io/docs/dev/healthcare/hl7/to-fhir/) - Converting v2 messages into FHIR bundles, codes and references included
- [EDIFACT](https://zato.io/docs/dev/healthcare/edifact/) - Parsing interchanges, dialects and the transports they arrive on
- [Transformation](https://zato.io/docs/dev/healthcare/transformation/) - One message in, another standard out, in ordinary Python
- [Audit log](https://zato.io/docs/dev/healthcare/audit-log.html) - Every message with its acknowledgment, searchable by patient identifier
- [AI in clinical interfaces](https://zato.io/docs/dev/healthcare/ai/) - Services calling LLMs and AI agents calling clinical services as tools
