Skip to main content

Including Linked Resources

In many cases, your application may need to search for not just one resource type, but also some of the related resources that those resources reference. For example, one might need to search for Observation resources with a certain code, but also the Patient resources on whom the observations were made, and the Provenance for the Observation resources as well. Using basic search requests, this would require many different API calls. The FHIR specification contains special _include and _revinclude search parameters, which return resources linked to the results of your search alongside in the same Bundle.

Comparison with GraphQL

In addition to these search parameters, Medplum also offers a FHIR GraphQL API that is suitable for retrieving linked resources when you only need to traverse a few hops in the resource graph and know the exact fields on each resource that you need. The major differences between the _(rev)include parameters and GraphQL are summarized below:

_(rev)includeGraphQL
Returns full resource JSONReturns only specified resource fields
Traverses resource references via search parametersTraverses references through special Reference.resource field
Can traverse up from a resource to one that references itCan only traverse down from a resource to one it references

_include and _revinclude

For example, the search query described above for Observation, Patient, and Provenance resources would look like this:

await medplum.searchResources('Observation', {
code: '78012-2',
_include: 'Observation:patient',
_revinclude: 'Provenance:target',
});
Example response
  [
{
resourceType: 'Observation',
id: '1',
meta: { versionId: 'b267aa05-e134-4f01-817a-5d255a691880', lastUpdated: '2022-12-21T01:55:34.799Z' },
status: 'final',
code: {
coding: [{ system: LOINC, code: '78012-2', display: 'Streptococcus pyogenes antigen, Throat' }],
},
valueCodeableConcept: {
coding: [{ code: '260385009', display: 'Negative', system: SNOMED }],
},
subject: { reference: 'Patient/1', display: 'Homer Simpson III' },
effectiveDateTime: '2022-11-01T19:33:00.000Z',
},
{
resourceType: 'Patient',
id: '1',
meta: {
versionId: '98a92482-bc3b-4f09-b7ce-08fea48fa135',
lastUpdated: '2023-03-22T03:05:21.361Z',
},
name: [{ given: ['Homer'], family: 'Simpson', suffix: ['III'] }],
gender: 'male',
birthDate: '1956-05-12',
},
{
resourceType: 'Provenance',
id: '1',
recorded: '2022-12-21T01:55:34.799Z',
target: [{ reference: 'Observation/1' }],
agent: [{ who: { reference: 'Practitioner/49d111f2-ae37-47bb-b8ee-2281d024501f' } }],
},
{
resourceType: 'Observation',
id: '2',
meta: { versionId: '7777208f-426f-41b1-ab4b-0eb6d3833f09', lastUpdated: '2023-05-01T00:00:00.000Z' },
status: 'final',
code: {
coding: [{ system: LOINC, code: '78012-2', display: 'Streptococcus pyogenes antigen, Throat' }],
},
valueCodeableConcept: {
coding: [{ code: '10828004', display: 'Positive', system: SNOMED }],
},
subject: { reference: 'Patient/1', display: 'Homer Simpson III' },
effectiveDateTime: '2023-02-04T11:45:00.000Z',
},
{
resourceType: 'Provenance',
id: '2',
recorded: '2022-12-21T01:55:34.799Z',
target: [{ reference: 'Observation/2' }],
agent: [{ who: { reference: 'Practitioner/49d111f2-ae37-47bb-b8ee-2281d024501f' } }],
},
{
resourceType: 'Observation',
id: '3',
status: 'final',
code: {
coding: [{ system: LOINC, code: '78012-2', display: 'Streptococcus pyogenes antigen, Throat' }],
},
valueCodeableConcept: {
coding: [{ code: '260385009', display: 'Negative', system: SNOMED }],
},
subject: { reference: 'Patient/2', display: 'Lisa Simpson' },
effectiveDateTime: '2022-06-12T16:03:00.000Z',
meta: { versionId: 'd4c4e4c7-a867-4b90-afd6-1c2bb84158de', lastUpdated: '2022-12-21T01:55:34.799Z' },
},
{
resourceType: 'Patient',
id: '2',
meta: {
versionId: '98a92482-bc3b-4f09-b7ce-08fea48fa135',
lastUpdated: '2023-03-22T03:05:21.361Z',
},
name: [{ given: ['Lisa'], family: 'Simpson' }],
gender: 'female',
birthDate: '2015-08-13',
},
{
resourceType: 'Provenance',
id: '3',
recorded: '2022-12-21T01:55:34.799Z',
target: [{ reference: 'Observation/3' }],
agent: [{ who: { reference: 'Practitioner/49d111f2-ae37-47bb-b8ee-2281d024501f' } }],
},
];
Note

The values of the _include and _revinclude parameters are not paths into the resource, but search parameters.

The _include=Observation:patient parameter adds to the search results all Patient resources referenced by the Observation.subject field of any of the original search results. Similarly, the _revinclude parameter adds any Provenance resources whose target field references one of the original search results. The full graph of resources returned in the search result Bundle could look something like this:

:iterate Modifier

By default, the _include and _revinclude only include resources one hop away from resources in the search results. In order to traverse subsequent reference links, add the :iterate modifier to the _include or _revinclude parameter. This will cause the inclusion to apply recursively, until no more resources are found. For example, to search for Observation resources, plus the Patient resources they refer to, plus those patients' associated Practitioner, you might use a search API call like this:

await medplum.searchResources('Observation', {
code: '78012-2',
_include: 'Observation:patient',
'_include:iterate': 'Patient:general-practitioner',
});
Example response
  [
{
resourceType: 'Observation',
id: '1',
meta: { versionId: 'b267aa05-e134-4f01-817a-5d255a691880', lastUpdated: '2022-12-21T01:55:34.799Z' },
status: 'final',
code: {
coding: [{ system: LOINC, code: '78012-2', display: 'Streptococcus pyogenes antigen, Throat' }],
},
valueCodeableConcept: {
coding: [{ code: '260385009', display: 'Negative', system: SNOMED }],
},
subject: { reference: 'Patient/1', display: 'Homer Simpson III' },
effectiveDateTime: '2022-11-01T19:33:00.000Z',
},
{
resourceType: 'Patient',
id: '1',
meta: { versionId: '98a92482-bc3b-4f09-b7ce-08fea48fa135', lastUpdated: '2023-03-22T03:05:21.361Z' },
name: [{ given: ['Homer'], family: 'Simpson', suffix: ['III'] }],
gender: 'male',
birthDate: '1956-05-12',
generalPractitioner: [{ reference: 'Practitioner/1' }],
},
{
resourceType: 'Practitioner',
id: '1',
name: [{ prefix: ['Dr.'], given: ['Julius', 'Michael'], family: 'Hibbert', suffix: ['M.D.'] }],
identifier: [{ system: 'http://hl7.org/fhir/sid/us-npi', value: '3141592654' }],
},
{
resourceType: 'Observation',
id: '2',
meta: { versionId: '7777208f-426f-41b1-ab4b-0eb6d3833f09', lastUpdated: '2023-05-01T00:00:00.000Z' },
status: 'final',
code: {
coding: [{ system: LOINC, code: '78012-2', display: 'Streptococcus pyogenes antigen, Throat' }],
},
valueCodeableConcept: {
coding: [{ code: '10828004', display: 'Positive', system: SNOMED }],
},
subject: { reference: 'Patient/1', display: 'Homer Simpson III' },
effectiveDateTime: '2023-02-04T11:45:00.000Z',
},
{
resourceType: 'Observation',
id: '3',
status: 'final',
code: {
coding: [{ system: LOINC, code: '78012-2', display: 'Streptococcus pyogenes antigen, Throat' }],
},
valueCodeableConcept: {
coding: [{ code: '260385009', display: 'Negative', system: SNOMED }],
},
subject: { reference: 'Patient/2', display: 'Lisa Simpson' },
effectiveDateTime: '2022-06-12T16:03:00.000Z',
meta: { versionId: 'd4c4e4c7-a867-4b90-afd6-1c2bb84158de', lastUpdated: '2022-12-21T01:55:34.799Z' },
},
{
resourceType: 'Patient',
id: '2',
meta: {
versionId: '98a92482-bc3b-4f09-b7ce-08fea48fa135',
lastUpdated: '2023-03-22T03:05:21.361Z',
},
name: [{ given: ['Lisa'], family: 'Simpson' }],
gender: 'female',
birthDate: '2015-08-13',
generalPractitioner: [{ reference: 'Practitioner/1' }],
},
];

This query would return a Bundle containing all the resources from a linked graph like the following:

Recursive application

The :iterate modifier applies recursively, and can return multiple levels of results. For example, all results in the graph shown below would be returned in the results for the following search API request:

GET /fhir/R4/Patient?_id=1&_include:iterate=Patient:link

Additional Examples

In some cases you may want to search for a Patient as well as any RelatedPerson resources they may have. This is often used in pediatric care when you need to retrieve a Patient and their parent in the same search. See the family relationships guide for more details.

Example: Get a Patient and their RelatedPerson
await medplum.searchResources('Patient', {
_id: 'lisa-simpson',
_revinclude: 'RelatedPerson:patient',
});

In this example we:

  • Search for a root Patient resource
  • We then use _revinclude=RelatedPerson:patient to include any RelatedPerson resources that reference one of the patients in our results.

However, some models use the RelatedPerson as a link between two patients (for more details see approach #3 of Family Relationships guide).

Example: Get a Patient and their RelatedPerson modeled as a Patient
await medplum.searchResources('Patient', {
_id: 'lisa-simpson',
_revinclude: 'RelatedPerson:patient',
'_revinclude:iterate': 'Patient:link',
});

This example is similar to the first, but includes an additional step.

  • We search for the root Patient resource
  • We use _revinclude=RelatedPerson:patient to again get any RelatedPerson resources that reference our results. In this case they are only representing a link between the two Patient resources.
  • We then use _revinclude:iterate=Patient:link to iterate on the RelatedPerson resources and get any Patient resources that reference them on the link element. In this scenario, these are the parents.

Searching for practitioners at a specific location

A common use case is searching for any Practitioner and PractitionerRole resources at a specific location. This can be necessary when the same physician provides care at multiple locations. See the provider organizations guide for more details.

Example: Find all practitioners and roles at a specific location
await medplum.searchResources('Location', {
_id: 'example-location',
_revinclude: 'PractitionerRole:location',
'_include:iterate': 'PractitionerRole:practitioner',
});

In this example, we:

  • Search for a root Location resource.
  • We then use _revinclude=PractitionerRole:location to get all PractitionerRole resources that reference the Location using the location search parameter.
  • Next, we iterate on the PractitionerRole resources using _include:iterate=PractitionerRole:practitioner. This will include all the Practitioner resources referenced by the practitioner search parameter of the returned PractitionerRoles.

Searching for a patient's care team

You may want to display a Patient resource as well as a list of all the members of their CareTeam on one page.

Example: Search for all members of the CareTeam of a Patient
await medplum.searchResources('Patient', {
_id: 'homer-simpson',
_revinclude: 'CareTeam:patient',
'_include:iterate': 'CareTeam:participant',
});

In this example we:

  • Search for a root Patient resource.
  • Then we use _revinclude=CareTeam:patient to search for a CareTeam resource that references our results using the patient search parameter.
  • We then iterate on the CareTeam with _include:iterate=CareTeam:participant to search for all members of the team.