FHIR Search

When working with the Oystehr 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 v3 SDK:

import { Patient } from 'fhir/r4b'; // npm install @types/fhir
import Oystehr from '@oystehr/sdk'
 
const oystehr = new Oystehr({
  accessToken: "<your_access_token>",
});
 
const patientBundle/*: Bundle<Patient>*/ = await oystehr.fhir.search<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: 'given',
      value: 'Jon',
    },
  ],
});
const patients/*: Patient[]*/ = patientBundle.unbundle();

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 Oystehr 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 Oystehr TypeScript SDK uses POST requests for all search queries, so you don't need to worry about URL length limits when using the SDK.

Total Count

When performing a FHIR search, the resulting Bundle includes a total attribute. This represents the total number of matches for the search, regardless of page size. By default this total is an estimate. You may want an exact count to facilitate exhaustive listing, or not to bother with a count at all. You can control this behavior by including the _total parameter in your search parameters:

  • none — Do not include a total attribute on the response Bundle
  • estimate — Include an estimated total on the response Bundle; this is the default behavior
  • accurate — Include an exact total for the search; searches using this parameter will see a performance penalty

For more information about this search parameter, see the FHIR Search documentation (opens in a new tab).

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 v3 SDK:

import { Patient } from 'fhir/r4b'; // npm install @types/fhir
import Oystehr from '@oystehr/sdk'
 
const oystehr = new Oystehr({
  accessToken: "<your_access_token>",
});
 
const patientBundle/*: Bundle<Patient>*/ = await oystehr.fhir.search<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: 'active:not',
      value: 'true',
    },
  ],
});
const patients/*: Patient[]*/ = patientBundle.unbundle();

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 v3 SDK:

import { Patient } from 'fhir/r4b'; // npm install @types/fhir
import Oystehr from '@oystehr/sdk'
 
const oystehr = new Oystehr({
  accessToken: "<your_access_token>",
});
 
const patientBundle/*: Bundle<Patient>*/ = oystehr.fhir.search<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: '_sort',
      value: 'given',
    },
  ],
});
const patients/*: Patient[]*/ = patientBundle.unbundle();

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 v3 SDK:

import { Patient } from 'fhir/r4b'; // npm install @types/fhir
import Oystehr from '@oystehr/sdk'
 
const oystehr = new Oystehr({
  accessToken: "<your_access_token>",
});
 
const patientBundle/*: Bundle<Patient>*/ = oystehr.fhir.search<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: '_count',
      value: '10',
    },
  ],
});
const patients/*: Patient[]*/ = patientBundle.unbundle();

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 v3 SDK:

import { Appointment } from 'fhir/r4b'; // npm install @types/fhir
import Oystehr from '@oystehr/sdk'
 
const oystehr = new Oystehr({
  accessToken: "<your_access_token>",
});
 
const bundle/*: Bundle<Appointment | Patient>*/ = await oystehr.fhir.search<Appointment | Patient>({
  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 v3 SDK:

import { Patient } from 'fhir/r4b'; // npm install @types/fhir
import Oystehr from '@oystehr/sdk'
 
const oystehr = new Oystehr({
  accessToken: "<your_access_token>",
});
 
const bundle/*: Bundle<Appointment | Patient>*/ = await oystehr.fhir.search<Appointment | 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 Oystehr from '@oystehr/sdk'
 
const oystehr = new Oystehr({
  accessToken: "<your access token>",
});
 
// corresponds to the SQL expression: LIKE "Anna Maria Juanita%"
const patients/*: Patient[]*/ = (await oystehr.fhir.search<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: 'name',
      value: 'Anna Maria Juanita',
    },
  ],
})).unbundle();
 
// corresponds to the SQL expression: = "Anna Maria Juanita"
const patients/*: Patient[]*/ = (await oystehr.fhir.search<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: 'name:exact',
      value: 'Anna Maria Juanita',
    },
  ],
})).unbundle();
 
// corresponds to the SQL expression: LIKE "%Anna Maria Juanita%"
const patients/*: Patient[]*/ = (await oystehr.fhir.search<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: 'name:contains',
      value: 'Anna Maria Juanita',
    },
  ],
})).unbundle();

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 Oystehr from '@oystehr/sdk'
 
const oystehr = new Oystehr({
  accessToken: "<your access token>",
});
 
// will return all resources that match Anna, Mari, or Juanita
const patients/*: Patient[]*/ = (await oystehr.fhir.search<Patient>({
  resourceType: 'Patient',
  params: [
    {
      name: 'name:exact',
      value: 'Anna,Maria,Juanita',
    },
  ],
})).unbundle();

Additional Resources