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:

import { Patient } from 'fhir/r4b'; // npm install @types/fhir
import zapehr from '@zapehr/sdk'
 
zapehr.init({
  ZAPEHR_ACCESS_TOKEN: "<your_access_token>",
});
 
const patients: Patient[] = await zapehr.fhir.list<Patient>({
  resourceType: 'Patient',
  params: [
    {
      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.

You can search for resources using a GET request, as in the examples above, or using a POST request. For POST requests, the URL must contain /:resource/_search, as opposed to /:resource, which is used for GET requests. The following example highlights this difference:

Example

Search for all patients named "Jon" with a GET request:

curl --location 'https://fhir-api.zapehr.com/r4/Patient?given=Jon' \
--header 'Authorization: Bearer <your_access_token>' \
--header 'x-zapehr-project-id: <your_project_id>'

Searches With Long Parameters

The ZapEHR FHIR API has a limit of 10KB for the length of a URL. Should you exceed this limit, your request will fail with HTTP Status Code 414. If you need to perform a search with a long parameter, or with many parameters, you can use a POST request to avoid the URL length limit. See the example above for cURL syntax.

The ZapEHR TypeScript SDK uses POST requests for all search queries, so you don't need to worry about URL length limits when using the SDK.

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:

import { Patient } from 'fhir/r4b'; // npm install @types/fhir
import zapehr from '@zapehr/sdk'
 
zapehr.init({
  ZAPEHR_ACCESS_TOKEN: "<your_access_token>",
});
 
const patients: Patient[] = await zapehr.fhir.list<Patient>({
  resourceType: 'Patient',
  params: [
    {
      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:

import { Patient } from 'fhir/r4b'; // npm install @types/fhir
import zapehr from '@zapehr/sdk'
 
zapehr.init({
  ZAPEHR_ACCESS_TOKEN: "<your_access_token>",
});
 
const patients: Patient[] = zapehr.fhir.list<Patient>({
  resourceType: 'Patient',
  params: [
    {
      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:

import { Patient } from 'fhir/r4b'; // npm install @types/fhir
import zapehr from '@zapehr/sdk'
 
zapehr.init({
  ZAPEHR_ACCESS_TOKEN: "<your_access_token>",
});
 
const patients: Patient[] = await zapehr.fhir.list<Patient>({
  resourceType: 'Patient',
  params: [
    {
      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:

import { Appointment } from 'fhir/r4b'; // npm install @types/fhir
import zapehr from '@zapehr/sdk'
 
zapehr.init({
  ZAPEHR_ACCESS_TOKEN: "<your_access_token>",
});
 
const appointments: Appointment[] = await zapehr.fhir.list<Appointment>({
  resourceType: 'Appointment',
  params: [
    {
      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:

import { Patient } from 'fhir/r4b'; // npm install @types/fhir
import zapehr from '@zapehr/sdk'
 
zapehr.init({
  ZAPEHR_ACCESS_TOKEN: "<your_access_token>",
});
 
const appointments: Appointment[] = await zapehr.fhir.list<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: '_revinclude',
      value: 'Appointment:patient',
    },
  ],
});

name Search Parameter

The name search parameter is used to match any of the string fields in a HumanName resource, including family, given, prefix, suffix, and/or text. A check on each field is performed separately, and a resource that has any matching field is added to the response.

Examples

import { Patient } from 'fhir/r4b'; // npm install @types/fhir
import zapehr from '@zapehr/sdk'
 
zapehr.init({
  ZAPEHR_ACCESS_TOKEN: "<your access token>",
});
 
// corresponds to the SQL expression: LIKE "Anna Maria Juanita%"
const patients: Patient[] = await zapehr.fhir.list<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: 'name',
      value: 'Anna Maria Juanita',
    },
  ],
});
 
// corresponds to the SQL expression: = "Anna Maria Juanita"
const patients: Patient[] = await zapehr.fhir.list<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: 'name:exact',
      value: 'Anna Maria Juanita',
    },
  ],
});
 
// corresponds to the SQL expression: LIKE "%Anna Maria Juanita%"
const patients: Patient[] = await zapehr.fhir.list<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: 'name:contains',
      value: 'Anna Maria Juanita',
    },
  ],
});

Additional Features

You can perform a search on several alternatives for the parameter value by using a comma separator (which is equal to combining results by OR).

Examples

import { Patient } from 'fhir/r4b'; // npm install @types/fhir
import zapehr from '@zapehr/sdk'
 
zapehr.init({
  ZAPEHR_ACCESS_TOKEN: "<your access token>",
});
 
// will return all resources that match Anna, Mari, or Juanita
const patients: Patient[] = await zapehr.fhir.list<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: 'name:exact',
      value: 'Anna,Maria,Juanita',
    },
  ],
});

Additional Resources