This page is part of the FHIR Specification (v5.0.0: R5 - STU). This is the current published version in it's permanent home (it will always be available at this URL). For a full list of available versions, see the Directory of published versions
FHIR Infrastructure Work Group | Maturity Level: N/A | Standards Status: Informative |
This page covers the workflows involved with the various 'search' mechanisms defined for use in FHIR. While the specific URL syntax varies for each of the different query options and, occasionally, the syntax of the response changes, the basics of the workflow is the same regardless of the search mechanism. There are two options: synchronous and asynchronous.
The synchronous search flow is similar to that in basic FHIR REST operations. The main difference with search is the introduction of paging to allow large result sets to be retrieved in reasonable-sized chunks.
1. The data consumer uses either a GET - where the search URL defines exactly what retrieval is to be invoked, along with any filters; or with POST - in which case the parameters are passed in the body of the HTTP call. The filters may also include desired page size. The contents of the URL vary by search mechanism, as will the format of response. Some of the search mechanisms may limit to only using GET or POST. The details will be discussed for each approach below.
2. The search response comes back in the body of the HTTP response. For most search mechanisms, this will be a search-set Bundle containing the results of the search. If no results are found, the result set will be empty, and the HTTP response will still be 200. If the payload is a search-set, the Bundle.total
will be set to 0. If there was a problem with the format of the search, with permissions or some other error, an appropriate HTTP code will be raised and optionally an OperationOutcome will be provided. The search response will also typically include links that allow navigation of the result set. For search-set Bundles, these will be the 'next', 'previous', 'first' and 'last' URLs conveyed in Bundle.link
. (Note that only relevant links will be provided and not all servers will support all link types - next and previous are commonly supported; first and last, less so.)
3. If navigation links were provided, this optional process allows the data consumer to navigate through the pages of the result set. Each new response will include links to navigate from that page.
Asynchronous search is identical to RESTful and operation asynchronous flows. Not all search approaches will support the asynchronous mechanism (this will be noted below in the section on each approach). Also, not all data sources will support the asynchronous mode, even if it is allowed. In an asynchronous request, there is no paging - all information is included in the file(s) produced by the asynchronous request
Step 1: The invocation of the operation asynchronously is identical to synchronous search invocation, with the exception that the Prefer
header is set to "respond-async". (Note that the server is not obligated to respect the client's stated preference - it could still respond synchronously.). The response includes a location-header that identifies where to monitor for the progress of the asynchronous operation.
Step 2: The data consumer polls the location specified in step 1 to see if the search is complete. So long as it is not, it gets back a 202 Accepted HTTP response, possibly with a message indicating the degree of progress. (Note: At any point, the data consumer could also cancel the request.)
Step 3: Once the search is complete, the response to the data consumer's polling request changes to a 200 and conveys other information, including URLs to the location of the file (or files) that contain the results of the operation.
Step 4: The data consumer retrieves the identified file or files from the data source, containing the search response.
In addition to the instructions on asynchronous exchange in the base spec, asynchronous search is described in more detail in the Bulk Data implementation guide. Implementers considering asynchronous communication should be familiar with this guide, as the capabilities it requires are much more likely to be widely implemented.
Details about workflow differences for each of the search mechanisms are listed below.
A history search is always invoked using GET. It can use [base]/_history
, [base]/[ResourceType]/_history
or [base]/[resourceType]/[id]/_history
, depending on whether the desired history is for the whole server, a particular resource type or for a single resource instance. A limited set of parameters are permitted to filter the set of history entries provided and to control the syntax of the response. The response is always a Bundle of type history
. Paging is supported.
A RESTful search can be invoked using GET or POST. In the latter case, the parameters are generally sent in the body as application/x-www-form-urlencoded content. However, a mixture where some parameters are on the POST URL and others are in the body is permitted. The base URL for the HTTP call can be either [base]
or [base]/[ResourceType]
or [base]/[resourceType]/[id]/_history
depending on whether the search is across multiple resource types or within a single resource. HL7 defines an extensive set of search parameters to both filter and extend the output. Additional filtering search parameters can also be defined by implementers or other specifications. The response is always a Bundle of type search-set
. Paging is supported.
A _filter search is identical to a regular RESTful search. The only difference is that one of the search parameters included (in the URL or body) is _filter.
GraphQL works similarly to RESTful search, but the syntax is different. Both GET and POST are permitted. For both GET and POST, the GraphQL JSON query string can be sent as a parameter in the query URL. With POST, it can also be sent in the body, with the ContentType set to application/graphql
. When using GraphQL, parameters must be exclusively sent in either the URL or the body - they are not permitted to appear in both locations. The base URL for the HTTP call can be one of [base]/$graphql
, [base]/[ResourceType]/[id]/$graphql
or [base]/.../$[operationName]?[parameters]&graphql=...
. The first allows a search against the whole repository. The second starts the search with a specific resource instance. The last allows GraphQL to execute against the results returned by the successful invocation of the operation - be it a resource, a Parameters instance or a search-set Bundle. The operation itself could be one executed at the base, type, or instance level.
The response to the search will be custom JSON - defined by the query itself. It can include full-blown resource structures, individual data elements or a combination of both. However, because it is not guaranteed to be FHIR-valid, it is not possible to invoke GraphQL as part of a batch.
If the Connection approach is used, then paging is possible. Asynchronous behavior is supported as it is for any FHIR operation.
There is not a formal mechanism for using CQL to perform search defined in the core FHIR specification. However, the draft version of the Clinical Quality Framework (CQF) IG defines a couple of operations allowing data to be retrieved by executing arbitrary CQL. There are two options. The cpg-cql operation executes CQL passed directly as an argument. The cpg-library-evaluate operation executes CQL specified in a referenced library.
The cpg-cql operation is invoked at the base URL ([base]/$cql
. It can potentially be invoked using GET if it does not use the parameters
parameter but can also use POST. The cpg-library-evaluate operation is invoked with the URL [base]/Library/[id]/$evaluate
can be invoked using GET provided that the parameters
parameter is not used. It can also be invoked with POST (with parameters past using a Parameters instance). Neither supports paging as the results aren't expected to come back in a search-set Bundle. Asynchronous execution is possible as for any FHIR operation.
There is no FHIR-specific mechanism for invoking a SPARQL query. The W3C defines a few ways to invoke a SPARQL query using a RESTful interface. These mechanisms correspond to the standard GET/POST query flows used for FHIR-defined queries. Other invocation mechanisms are also possible, including the potential use of a FHIR operation. (No standard operation has yet been defined.) Because FHIR search-set Bundles are not involved, there is no query continuation mechanism. It is possible that the FHIR async mechanism could be used when invoking SPARQL, but if so, this would be a custom behavior outside the scope of FHIR - as SPARQL invocation itself is outside the scope of FHIR.
The _query mechanism works in a similar manner to RESTful search. It can be invoked either at the [base]?_query=name
or [base]/[resourceType]?_query=name
level. Depending on the nature of the parameters defined by the named operation, it may be possible to invoke it using GET. It can always be invoked using POST - where the body is a Parameters instance. The response (if successful) is a search-set Bundle. That bundle may include links that support paging. Asynchronous invocation is supported.