Bundle — Batch & Transaction
Couchbase FHIR CE supports FHIR Bundles to perform multiple operations in a single request. Endpoint (note: no resource type in the path):
Example
POST /fhir/<bucket>/
e.g. POST http://localhost/fhir/acme/
JSON Body
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{
"fullUrl": "urn:uuid:3da658f0-76ae-4ddd-94ce-4cdbb12a1e8e",
"resource": {
"resourceType": "Patient",
"id": "3da658f0-76ae-4ddd-94ce-4cdbb12a1e8e",
"identifier": [
{
"system": "http://hospital.smarthealthit.org",
"value": "MRN001"
}
],
"name": [
{
"use": "official",
"family": "Doe",
"given": ["Jane"]
}
],
"gender": "female",
"birthDate": "1970-12-01"
},
"request": {
"method": "POST",
"url": "Patient"
}
},
{
"fullUrl": "urn:uuid:ad8085ce-fbf7-442b-99e5-f9f02e7dcd92",
"resource": {
"resourceType": "Observation",
"status": "final",
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
"code": "vital-signs",
"display": "Vital Signs"
}
]
}
],
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "85354-9",
"display": "Blood pressure panel with all children optional"
}
]
},
"subject": {
"reference": "urn:uuid:3da658f0-76ae-4ddd-94ce-4cdbb12a1e8e"
},
"effectiveDateTime": "2023-06-01T12:00:00Z",
"component": [
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "8480-6",
"display": "Systolic blood pressure"
}
]
},
"valueQuantity": {
"value": 120,
"unit": "mmHg"
}
},
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "8462-4",
"display": "Diastolic blood pressure"
}
]
},
"valueQuantity": {
"value": 80,
"unit": "mmHg"
}
}
]
},
"request": {
"method": "POST",
"url": "Observation"
}
}
]
}
Response - 200 OK
{
"resourceType": "Bundle",
"id": "5ab72487-9469-4240-a3a0-b418b1b1e7c3",
"meta": {
"lastUpdated": "2025-10-09T19:54:36.095+00:00"
},
"type": "transaction-response",
"timestamp": "2025-10-09T19:54:36.095+00:00",
"link": [
{
"relation": "self",
"url": "http://localhost/fhir/acme/"
}
],
"entry": [
{
"resource": {
"resourceType": "Patient",
"id": "8c350e25-1640-421b-a1e1-e6d077873728",
"meta": {
"versionId": "1",
"lastUpdated": "2025-10-09T19:54:35.974+00:00",
"tag": [
{
"system": "http://couchbase.fhir.com/fhir/custom-tags",
"code": "created-by",
"display": "user:anonymous"
}
]
},
"identifier": [
{
"system": "http://hospital.smarthealthit.org",
"value": "MRN001"
}
],
"name": [
{
"use": "official",
"family": "Doe",
"given": ["Jane"]
}
],
"gender": "female",
"birthDate": "1970-12-01"
},
"response": {
"status": "201 Created",
"location": "Patient/8c350e25-1640-421b-a1e1-e6d077873728"
}
},
{
"resource": {
"resourceType": "Observation",
"id": "e00a7f2e-1a42-446b-ba5a-9f412afc4fe2",
"meta": {
"versionId": "1",
"lastUpdated": "2025-10-09T19:54:36.050+00:00",
"tag": [
{
"system": "http://couchbase.fhir.com/fhir/custom-tags",
"code": "created-by",
"display": "user:anonymous"
}
]
},
"status": "final",
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
"code": "vital-signs",
"display": "Vital Signs"
}
]
}
],
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "85354-9",
"display": "Blood pressure panel with all children optional"
}
]
},
"subject": {
"reference": "Patient/8c350e25-1640-421b-a1e1-e6d077873728"
},
"effectiveDateTime": "2023-06-01T12:00:00Z",
"component": [
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "8480-6",
"display": "Systolic blood pressure"
}
]
},
"valueQuantity": {
"value": 120,
"unit": "mmHg"
}
},
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "8462-4",
"display": "Diastolic blood pressure"
}
]
},
"valueQuantity": {
"value": 80,
"unit": "mmHg"
}
}
]
},
"response": {
"status": "201 Created",
"location": "Observation/e00a7f2e-1a42-446b-ba5a-9f412afc4fe2"
}
}
]
}
Bundle Types
type: "transaction"
- Atomic, all-or-nothing. The server processes the entries as a single unit:
- If all succeed → commit.
- If any fails → none are applied; the response includes the failure.
Response: type:
"transaction-response" with one entry.response per input entry.
type: "batch"
- Non-atomic. Entries are executed independently; some may succeed while others fail.
- Order is server-defined (no cross-entry guarantees).
Response: type:
"batch-response" with one entry.response per input entry.
Entry Anatomy
Each entry typically has:
- fullUrl: a temporary identifier for this entry’s resource. Commonly a URN such as urn:uuid:guid used to link entries together before real IDs exist.
- resource: the FHIR resource payload (for methods that send a body).
- request: the operation to perform:
- method: POST, PUT, PATCH, DELETE, or GET
- url: relative interaction URL such as Patient, Patient/123, Observation?code=…, etc.
URN / fullUrl resolution
Within the same bundle, you can reference another entry’s fullUrl. During processing the server:
- Creates the resource (assigning server ID for POST).
- Builds a mapping from fullUrl → the final canonical reference (e.g., Patient/8c350e25-…).
- Rewrites internal references to canonical form.
In our example, the Observation’s subject.reference changes from urn:uuid:3da6… → Patient/8c350e25-1640-421b-a1e1-e6d077873728.
Processing Model
- For transaction bundles, Couchbase FHIR CE executes the entire bundle inside a single ACID transaction over Couchbase:
- Collect & validate entries (FHIR R4 rules, plus US Core if enabled for the bucket).
- If validation:
- Strict → any violation aborts the transaction.
- Lenient → warnings logged; proceed.
- Disabled → skip validation.
- Resolve dependencies using fullUrl mapping and any conditional URLs (e.g., PUT Patient?identifier=…).
- Apply each request in a safe order (e.g., create Patient before Observation that references it).
- POST: server assigns a new UUID id.
- PUT: preserves/sets the ID from the URL; creates or updates.
- PATCH: applies JSON Patch (RFC 6902).
- DELETE: soft-deletes (tombstone).
- GET: allowed in bundles; returns the matched resource in the response entry.
Versioning & audit
- meta.versionId increments per write.
- meta.lastUpdated set by server.
- meta.tag set (created-by / updated-by).
- Prior versions copied to Resources.Versions.
Commit / rollback
- Transaction bundles: either everything commits or everything rolls back.
- Batch bundles: no overall transaction; each entry stands alone.
Isolation & consistency
- Transaction bundles run with ACID semantics across the participating KV/N1QL operations.
- Practical effect: readers won’t see partial results; writers see a consistent snapshot of documents involved.
- No READ-UNCOMMITTED.
FTS
FTS itself is not transactional; searches reflect the committed state once the transaction completes and the index refreshes.
Response Shape
Top-level Bundle with:
- type: "transaction-response" or "batch-response"
- entry[]: one per input, each with:
- response.status (e.g., 201 Created, 200 OK, 204 No Content, 404 Not Found, 412 Precondition Failed)
- response.location (canonical location for created/updated resources)
- Optional resource (when Prefer: return=representation or when the interaction returns a body, e.g., GET)
Our sample shows:
- Patient created → 201 Created, location Patient/id
- Observation created → 201 Created, with subject rewritten to the new Patient ID
Conditionals inside Bundles
You can use conditional interactions within entries:
- Conditional PUT: PUT Patient?identifier=system|value
- 1 match → replace; 0 → create; >1 → 412
- Conditional PATCH: PATCH Observation?identifier=…
- 1 match → patch; 0 → 404; >1 → 412
- Conditional DELETE / READ similarly follow FHIR rules.
- The server resolves conditionals as part of the transaction, so either all cross-references are fixed and applied, or none are.
Validation & Profiles
- Base FHIR R4 validation always available.
- If the bucket enables US Core 6.1.0, profile rules and ValueSet bindings are enforced per your Strict / Lenient / Disabled setting.
- Failures in Strict abort a transaction bundle.
Best Practices
- Prefer transaction when creating graph-connected resources (Patient + related Observations), so references are resolved atomically.
- Use fullUrl: "urn:uuid:..." for intra-bundle linking; avoid guessing IDs.
- For upserts, prefer conditional PUT with a unique identifier rather than name.
- Keep bundles reasonable in size for performance (e.g., tens to low hundreds of entries).
- For responses, add header Prefer: return=representation if you want the full created/updated resources echoed in each entry.resource.
Summary
- Batch = independent operations, non-atomic.
- Transaction = atomic ACID unit; reference rewriting, versioning, and validation all happen together.
- fullUrl + URNs let you build self-contained graphs that the server resolves to canonical references in one step.
- The endpoint is the bucket root (/fhir/bucket/), not a resource type.
- Fully aligned with FHIR R4 semantics; US Core rules apply when enabled.