Bulk Operations using Bundles
In previous lessons, we explored how to perform CRUD operations on FHIR resources using the REST API. But what if you need to perform multiple operations together?
This is where the Bundle resource comes in. Let’s take a look at how to use bundles, specifically batch and transaction bundles, to group requests and handle them in a single operation.
What is a FHIR Bundle?
A Bundle is a container for a collection of resources.
You’ve already seen bundles before, for example, when you search for all patients, the server responds with a Bundle of type searchset
containing all matching resources.
But bundles can also be used to:
- Create, update, or delete multiple resources in one request.
- Execute multiple requests as either independent actions or as a single atomic transaction.
Batch Bundle
A batch bundle allows you to group multiple requests together. Each entry in the bundle specifies:
- The resource (optional, depending on the method).
- The request details: HTTP method (GET, POST, PUT, DELETE) and URL.
Example: Create a patient and fetch all patients
{
"resourceType": "Bundle",
"type": "batch",
"entry": [
{
"resource": {
"resourceType": "Patient",
"name": [{ "family": "Ramesh", "given": ["Sidharth"] }],
"gender": "male",
"birthDate": "1997-11-11"
},
"request": {
"method": "POST",
"url": "Patient"
}
},
{
"request": {
"method": "GET",
"url": "Patient"
}
}
]
}
- The first entry creates a patient.
- The second entry fetches all patients.
When sent to the FHIR server as a POST [base]/ request, the server executes both requests and returns a bundle of type batch-response
.
With a batch, some requests may succeed while others fail. For example, if you create an Observation linked to a patient that doesn’t exist, the Observation request may fail, but the other requests will still execute.
Transaction Bundle
A transaction bundle ensures all requests succeed together, or none succeed at all. This is useful when multiple resources are related and you need to maintain consistency (atomicity).
Example: Patient with Observations
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{
"resource": {
"resourceType": "Patient",
"id": "example-patient",
"name": [{ "family": "Doe", "given": ["Jane"] }]
},
"request": {
"method": "POST",
"url": "Patient"
}
},
{
"resource": {
"resourceType": "Observation",
"subject": { "reference": "Patient/example-patient" },
"status": "final",
"code": { "text": "Blood Pressure" }
},
"request": {
"method": "POST",
"url": "Observation"
}
}
]
}
Here, if the Patient creation fails, the Observation creation will also be rolled back. This guarantees atomicity, similar to how database transactions work.
Conclusion
Batch is useful for efficiency when operations don’t depend on each other whereas transaction is critical for data consistency when resources are interlinked (e.g., creating a patient along with encounters, conditions, or observations).
FHIR’s use of bundles ensures flexibility, whether you need loose grouping or strict atomicity, bundles give you the control.