Contained Resources in FHIR – a definitive guide

By: Amos (Kippi) Bordowitz

One of the most basic principles in FHIR® is the concept of Resources – distinct, well defined building blocks that represent common data entities that are used in healthcare. The formal definition of a resource includes its structure (that may be represented as JSON, XML or RDF), its scope and meaning, and a set of optional elements that are common to all resources (id, meta and extensions, to name a few).

The correct placement of each data element in the correct resource type is crucial when working with FHIR, since this is the basis for the standard’s flexibility - the ability to support the widest possible range of use-cases with the same basic set of well-defined resources. This is the principle that makes FHIR a good platform for interoperability, but it is also a principle that sometimes adds to the complexity of the solution, and this is where a temptation to break the rules and “cut corners” may arise.

A big part of the complexity comes from another principle in FHIR, or more specifically in FHIR’s RESTful exchange framework, and that is that a resource SHALL have a known identity and location, both represented by a single Uniform Resource Locator (URL). This URL (relative or absolute) is what we use to connect resources together to create a coherent representation of a scenario in healthcare. This URL is also the HTTP endpoint that can be used to retrieve the resource and manipulate it.

So, what happens when the creation of a resource with its own URL is not possible (e.g., in that part of the workflow where a FHIR server isn’t accessible, or it doesn’t support the creation if this specific resource type), or not desired? Maybe we don’t want this resource to have a “life of its own” – we don’t want anyone to access, manipulate or refer to it directly.

For example, we may want to order an ambulance to a specific address, so we start by creating a ServiceRequest resource. But when we try to find where to represent the address, we realize it requires the creation of another resource – Location. But this Location resource is only needed because we want to represent an address in a ServiceRequest; it does not represent a location that anyone else should refer to in other use-cases, and we especially don’t want anyone else to change it or delete it. It also does not have any useful identifier or name. In other words – this Location resource is only a technical container for data needed in another resource, and it should not become a part of the “catalog” of Location resources available on the FHIR server.

Other common scenarios are encountered when using FHIR for inter-organizational transfer of data. In these cases, we often need to transfer some data about the Patient or some other base entity (Practitioner, Organization etc.) as part of a transaction (for example, a bundle containing some MedicationRequests issued during an Encounter), but this data is not the “master” data, it is a secondary source of data that already exists in the target organization’s FHIR server. If we go strictly by the rules, the source organization should search for the base resource in the target organization’s server, and then reference the resource using its URL. But then we cannot transfer the secondary data – it stays in the source organization’s systems only. In many healthcare scenarios this is not desired – we want to transfer the complete data set, as it is recorded in the source system, while not overriding the “master” copy of the same resource in the destination.

For those cases FHIR defines a special way of representing and referencing resources – Contained resources. As the name implies, contained resources are resources that only exist inside other resources. They do not have their own URL, they cannot be accessed or manipulated directly, and they cannot be used in any way outside of the scope of their containing resource.

If we go back to the Ambulance example above – we can put a basic Location resource “inside” the ServiceRequest resource, and reference it internally. This way the address is passed along with the ServiceRequest without creating a new Location resource on the FHIR server.

If we take the second example – we can use contained resources to pass the secondary data about the patient or practitioner and reference those internally, without overriding the “master” copies of those resources in the target server, and without creating duplicate resources for the same patient or practitioner.

The contained resource solution can also be used when the referenced resource doesn’t really exist on any FHIR server, or when we have no way to know its URL.

Example (from HL7):

{

  "resourceType" : "Condition",

  "contained": [

    {

      "resourceType" : "Practitioner",

      "id" : "p1",

      "name" : [{

        "family" : "Person",

        "given" : ["Patricia"]

      }]

          }],

   "asserter" : {

     "reference" : "#p1"

  }

}

In this example, “Condition” is the main (containing) resource. “Practitioner” is the contained resource, which must have an id for reference. This id needs only to be unique within the scope of the containing resource, so we can use a generic id like “practitioner” or “p1”, “p2” etc. The structure of the contained resource is a “Practitioner” structure and must abide by its rules. In addition to the inclusion of the contained resource in the “contained” array, a reference will be made to it from the containing resource so there is no ambiguity as to its role, significance, and context.

Some more notes:

·       Contained resources SHALL NOT contain additional contained resources.

·       Contained resources cannot contain meta except meta.tag

·       The contained resource can’t be referred to outside the main resource, not even from another resource in the same bundle.

·       When creating instances in FHIR Shorthand (FSH), we can generate a contained resource instance by declaring its usage as “#inline”. This will let SUSHI know it should not export this resource as a separate JSON file, but only hold it in memory for use inside another instance’s “contained” array

Follow us on LinkedIn

Want to train your organization? Check out our Academy page

More To Explore