FHIR Search

When working with the ZapEHR FHIR API you will regularly need to search for resources matching a search query. Search allows you to join, filter, sort, and so much more (opens in a new tab).

To get you started with FHIR search, we'll go over the most common search parameters and how to use them.

Search Basics

To perform a search query, you need a resource type and a search parameter. For example, to search for all patients with the name "Jon", you would would look up the appropriate search parameter in the Patient resource documentation (opens in a new tab). In this case, the search parameter is given.

You can then use the search parameter in a search query with either the API or SDK:

Search for all patients named "Jon" with the SDK:

const fhirClient = new FhirClient({
  accessToken: TEST_ACCESS_TOKEN,
});
 
const patients: Patient[] = await fhirClient.searchResources({
  resourceType: 'Patient',
  searchParams: [
    {
      name: 'given',
      value: 'Jon',
    },
  ],
});

Successful search queries will return a Bundle (opens in a new tab) resource containing the matching resources. The Bundle resource is used to convey an array of FHIR resources. As an example, the above query might return the following response:

{
  "resourceType": "Bundle",
  "type": "searchset",
  "entry": [
    {
      "fullUrl": "https://fhir-api.zapehr.com/Patient/6bbdbadf-4f60-4bdd-8c54-b37970c584b2",
      "resource": {
        "resourceType": "Patient",
        "active": true,
        "name": [
          {
            "given": ["Jon"],
            "family": "Snow"
          }
        ],
        "id": "6bbdbadf-4f60-4bdd-8c54-b37970c584b2",
        "meta": {
          "versionId": "ef4e3e20-4103-4df5-8f89-b4f49d6310b3",
          "lastUpdated": "2023-11-15T18:11:31.740Z"
        }
      },
      "search": {
        "mode": "match"
      }
    }
  ],
  "link": [
    {
      "relation": "self",
      "url": "https://testing.fhir-api.zapehr.com/Patient?_count=20&_elements=&given=Jon"
    },
    {
      "relation": "first",
      "url": "https://testing.fhir-api.zapehr.com/Patient?_count=20&_elements=&_offset=0&given=Jon"
    }
  ]
}

Any resources matching the search criteria will be included in the entry array. In this case, there is only one patient named "Jon" in the system, so only one resource is returned.

The link array contains relations like first, prev, next, and last which you can use to fetch other pages when there was more matching data than was returned in the query. In this case we only have one page of results, so only self and first are returned.

Search Modifiers

Search modifiers are used to modify the behavior of a search parameter. For example, you can use the not modifier to search for a resource that does not have a certain value for a field. Modifiers are appended to the end of a search parameter with a colon. For example, to search for all patients that are not active, you would use the not modifier on the active search parameter:

Search for all patients that are not active using the SDK:

const fhirClient = new FhirClient({
  accessToken: TEST_ACCESS_TOKEN,
});
 
const patients: Patient[] = await fhirClient.searchResources({
  resourceType: 'Patient',
  searchParams: [
    {
      name: 'active:not',
      value: 'true',
    },
  ],
});

Here are some of the most frequently used modifiers:

  • :not — Search for resources that do not have a certain value for a "token" property. Token (opens in a new tab) search parameters are used when searching for an exact match for a code or identifier.
  • :exact — Search for resources that have an exact match for a "string" property.
  • :contains — Search for resources that contain a certain value for a "string" or "uri" property.
  • :missing — Search for resources that do not have a certain property.

The full list of modifiers can be found in the FHIR Search documentation (opens in a new tab).

Sorting

You can sort the results of a search query by using the _sort parameter. For example, to sort patients alphabetically by their given name, you would use the given search parameter with the _sort parameter:

Search for all patients sorted by given name using the SDK:

const fhirClient = new FhirClient({
  accessToken: TEST_ACCESS_TOKEN,
});
 
const patients: Patient[] = await fhirClient.searchResources({
  resourceType: 'Patient',
  searchParams: [
    {
      name: '_sort',
      value: 'given',
    },
  ],
});

Sort the opposite direction by adding a - before the parameter name like, _sort=-given.

You can sort by multiple parameters by separating them with a comma, _sort=given,family.

Limiting Page Size

Individual FHIR resources can be quite large, so to keep searches running quickly, you may want to limit the number of resources returned in one page. You can do this with the _count parameter:

Search for all patients returning only ten in each page using the SDK:

const fhirClient = new FhirClient({
  accessToken: TEST_ACCESS_TOKEN,
});
 
const patients: Patient[] = await fhirClient.searchResources({
  resourceType: 'Patient',
  searchParams: [
    {
      name: '_count',
      value: '10',
    },
  ],
});

You can get the next page of results by using the link relation next, as described in the Search Basics section.

Joining Referenced Resources

The _include and _revinclude parameters enable returning referenced resources in search results. You can think of this like a JOIN in SQL.

For example, if you were building an appointments feature in an EHR, you might want to query up appointments to show a calendar of upcoming appointments. For every appointment you query with your search, you also want the information about the patient the appointment is for. You can do this with the _include parameter:

Search for appointments joining in their referenced patients with the SDK:

const fhirClient = new FhirClient({
  accessToken: TEST_ACCESS_TOKEN,
});
 
const appointments: Appointment[] = await fhirClient.searchResources({
  resourceType: 'Appointment',
  searchParams: [
    {
      name: '_include',
      value: 'Appointment:patient',
    },
  ],
});

The above query will return a Bundle containing all appointments, and for each appointment, the patient resource will be included in the entry array. The patient resource will be included in the entry array, and its search.mode will be include:

{
  "resourceType": "Bundle",
  "type": "searchset",
  "entry": [
    {
      "fullUrl": "https://fhir-api.zapehr.com/Appointment/2abde332-dd3e-4e3f-a317-4652cf32d991",
      "resource": {
        "resourceType": "Appointment",
        "status": "booked",
        "start": "2023-12-10T09:00:00Z",
        "end": "2023-12-10T10:00:00Z",
        "participant": [
          {
            "actor": {
              "reference": "Patient/542754df-9215-4b54-ac6e-870eaffed1af"
            },
            "required": "required",
            "status": "accepted"
          }
        ],
        "id": "2abde332-dd3e-4e3f-a317-4652cf32d991",
        "meta": {
          "versionId": "1ca1ef9a-acd1-499a-8ebe-ed60e5b7ca4f",
          "lastUpdated": "2023-11-16T04:02:06.227Z"
        }
      },
      "search": {
        "mode": "match"
      }
    },
    {
      "fullUrl": "https://fhir-api.zapehr.com/Patient/542754df-9215-4b54-ac6e-870eaffed1af",
      "resource": {
        "resourceType": "Patient",
        "active": true,
        "name": [
          {
            "given": [
              "Jon"
            ],
            "family": "Snow"
          }
        ],
        "address": [
          {
            "use": "home",
            "type": "physical",
            "text": "Winterfell"
          }
        ],
        "id": "542754df-9215-4b54-ac6e-870eaffed1af",
        "meta": {
          "versionId": "c2047012-a48c-44e8-a77b-cdb3c2ba7fea",
          "lastUpdated": "2023-11-16T03:24:31.230Z"
        }
      },
      "search": {
        "mode": "include"
      }
    }
  ],
  ...
}

_revinclude is just like _include, except it returns resources that reference the resource you are searching for. If, when searching for Patients, you wanted to also return Appointments that reference those patients, you could use _revinclude. You could get basically the same result as above by searching for patients and "_revincluding" their appointments:

Search for patients joining in appointments which reference them with the SDK:

const fhirClient = new FhirClient({
  accessToken: TEST_ACCESS_TOKEN,
});
 
const appointments: Appointment[] = await fhirClient.searchResources({
  resourceType: 'Patient',
  searchParams: [
    {
      name: '_revinclude',
      value: 'Appointment:patient',
    },
  ],
});

Additional Resources