Referencing Resources in Transactions
When creating multiple resources by using a FHIR Bundle, sometimes you have to refer to a resource that hasn’t yet been created in the FHIR server.
That poses a challenge. How do you reference an ID that hasn’t been assigned?
This is a common problem when using FHIR, and in this tutorial we will look at two approaches, using hardcoded IDs and using temporary references.
Approach 1: Hardcode an ID and use it with PUT
The first method is to assign an ID yourself when creating a resource. This is done by using a PUT request and specifying the chosen ID in the URL.
For example, here is a batch
Bundle where we create a Patient with ID 123
, and also create an Observation that references the patient.
{
"resourceType": "Bundle",
"type": "batch",
"entry": [
{
"request": {
"method": "PUT",
"url": "Patient/123"
},
"resource": {
"resourceType": "Patient",
"id": "123",
"name": [
{
"given": ["Sidharth"],
"family": "Ramesh"
}
],
"telecom": [
{
"system": "email",
"value": "sidharth@medblocks.com"
}
]
}
},
{
"resource": {
"resourceType": "Observation",
"status": "final",
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "4548-4",
"display": "Hemoglobin A1c/Hemoglobin.total in Blood"
}
]
},
"valueQuantity": {
"value": 6.3,
"unit": "%",
"system": "http://unitsofmeasure.org",
"code": "%"
},
"subject": {
"reference": "Patient/123"
}
},
"request": {
"method": "POST"
}
}
]
}
This approach has the following drawbacks,
- Not all servers allow client-assigned IDs
- Many servers enforce rules about valid ID formats
- You might run into conflicts if the ID is already taken
- If one request fails, the bundle may execute inconsistently
Approach 2: [Recommended] Use a transaction
bundle with temporary references
The preferred and safer method is to use a transaction
bundle, and assign temporary references in the fullUrl
field.
Here’s how it works,
- Assign each resource a unique, temporary
fullUrl
(e.g.urn:uuid:patient-123
) - Reference that
fullUrl
in related resources (like Observations) - When the Bundle is processed, the server replaces temporary references with real IDs
Also, since we are using a transaction, all resources succeed or fail, which ensures consistency.
Here’s an example of a transaction bundle that uses fullUrl
,
{
"resourceType": "Bundle",
"type": "batch",
"entry": [
{
"fullUrl": "urn:uuid:patient-123"
"request": {
"method": "POST"
},
"resource": {
"resourceType": "Patient",
"name": [
{
"given": ["Sidharth"],
"family": "Ramesh"
}
],
"telecom": [
{
"system": "email",
"value": "sidharth@medblocks.com"
}
]
}
},
{
"resource": {
"resourceType": "Observation",
"status": "final",
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "4548-4",
"display": "Hemoglobin A1c/Hemoglobin.total in Blood"
}
]
},
"valueQuantity": {
"value": 6.3,
"unit": "%",
"system": "http://unitsofmeasure.org",
"code": "%"
},
"subject": {
"reference": "Patient/urn:uuid:patient-123"
}
},
"request": {
"method": "POST"
}
}
]
}
To submit the above bundle to the server, use the base URL,
POST https://fhir-bootcamp.medblocks.com/fhir
On analysing the server response, you can see that a real ID is assigned to the Patient, and Observation.subject
is updated to reference this new Patient ID.
Since we used a transaction
the resources are created atomically, that is either all succeed or all fail.
Summary
When creating related resources in a FHIR Bundle,
- You can hardcode IDs with
PUT
, but this is fragile and sometimes not supported - The recommended approach is to use a
transaction
Bundle with temporary references specified viafullUrl
This ensures that resource creation happens atomically (all-or-nothing), server-assigned IDs are safely resolved and referential integrity is preserved.