Skip to main content

Bundles

Ignixa supports FHIR Bundle resources for submitting multiple operations in a single request.

Batch vs Transaction

Choose the right bundle type for your use case:

BatchTransaction
AtomicityNone - each entry independentAll-or-nothing
On failureOther entries still succeedAll entries rolled back
ExecutionParallel (faster)Sequential by verb order
urn:uuid referencesNot resolved between entriesResolved across entries
Use whenBulk loading independent dataCreating related resources together

Batch Bundles

Batch bundles execute each entry independently. Failures in one entry don't affect others.

{
"resourceType": "Bundle",
"type": "batch",
"entry": [
{
"request": { "method": "POST", "url": "Patient" },
"resource": { "resourceType": "Patient", "name": [{"family": "Smith"}] }
},
{
"request": { "method": "POST", "url": "Patient" },
"resource": { "resourceType": "Patient", "name": [{"family": "Jones"}] }
},
{
"request": { "method": "GET", "url": "Patient/123" }
}
]
}

Batch Response

Each entry gets its own status - some may succeed while others fail:

{
"resourceType": "Bundle",
"type": "batch-response",
"entry": [
{ "response": { "status": "201 Created", "location": "Patient/abc" } },
{ "response": { "status": "201 Created", "location": "Patient/def" } },
{ "response": { "status": "404 Not Found" } }
]
}

When to Use Batch

  • Loading large datasets where entries are independent
  • Fetching multiple resources in one request
  • Operations where partial success is acceptable
  • Performance-critical bulk operations (parallel execution)

Transaction Bundles

Transaction bundles are atomic - all entries succeed or all are rolled back. Use when creating related resources that reference each other.

{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{
"fullUrl": "urn:uuid:patient-1",
"request": { "method": "POST", "url": "Patient" },
"resource": {
"resourceType": "Patient",
"name": [{"family": "Smith"}]
}
},
{
"fullUrl": "urn:uuid:encounter-1",
"request": { "method": "POST", "url": "Encounter" },
"resource": {
"resourceType": "Encounter",
"status": "in-progress",
"class": { "code": "AMB" },
"subject": { "reference": "urn:uuid:patient-1" }
}
},
{
"fullUrl": "urn:uuid:obs-1",
"request": { "method": "POST", "url": "Observation" },
"resource": {
"resourceType": "Observation",
"status": "final",
"code": { "coding": [{"system": "http://loinc.org", "code": "8310-5"}] },
"subject": { "reference": "urn:uuid:patient-1" },
"encounter": { "reference": "urn:uuid:encounter-1" }
}
}
]
}

urn:uuid Reference Resolution

The urn:uuid:patient-1 temporary reference is resolved to the actual Patient ID after creation. The Observation's subject.reference becomes "Patient/abc123" in the stored resource.

Transaction Response

On success, all entries return their status:

{
"resourceType": "Bundle",
"type": "transaction-response",
"entry": [
{ "response": { "status": "201 Created", "location": "Patient/abc123" } },
{ "response": { "status": "201 Created", "location": "Encounter/def456" } },
{ "response": { "status": "201 Created", "location": "Observation/ghi789" } }
]
}

On failure, the entire transaction is rolled back:

{
"resourceType": "Bundle",
"type": "transaction-response",
"entry": [{
"response": {
"status": "400",
"outcome": {
"resourceType": "OperationOutcome",
"issue": [{ "severity": "error", "diagnostics": "Validation failed..." }]
}
}
}]
}

Processing Order

Transaction entries are reordered by HTTP verb for consistent outcomes:

OrderVerbWhy
1DELETERemove before recreating
2POSTCreate new resources
3PUTUpdate existing
4PATCHPartial updates
5GETReads last

When to Use Transaction

  • Creating a Patient with related Observations, Encounters, etc.
  • Ensuring referential integrity between resources
  • Operations that must all succeed or all fail
  • Workflows requiring atomicity

Conditional Operations

Both bundle types support conditional operations:

{
"request": {
"method": "PUT",
"url": "Patient?identifier=http://example.org|12345"
},
"resource": { "resourceType": "Patient", ... }
}

This updates the Patient matching the identifier, or creates if not found.

Streaming Architecture

Ignixa processes bundles using a streaming approach:

Phase 1 (Streaming): Entries without urn:uuid references execute immediately in parallel as they're parsed.

Phase 2 (Buffered): When urn:uuid or conditional references are detected, remaining entries are buffered for reference resolution before execution.

This provides:

  • Memory efficiency - entries processed as they arrive
  • Lower latency - processing starts before full request received
  • Optimal throughput - parallel execution when possible

Bundle responses place link elements after entry because streaming serialization writes entries as they complete - pagination links can only be determined after all entries are processed.

Best Practices

ScenarioUse
Loading 1000 independent Patient recordsBatch (or bulk import)
Creating Patient + Observation + Encounter togetherTransaction
Fetching 10 resources by IDBatch
Updating resources that reference each otherTransaction

Limitations

  • Large bundles: For 1000+ entries, consider bulk import
  • urn:uuid in batch: References between batch entries are not resolved
  • Parallel conflicts: Conditional creates in parallel may race