Release 5

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 icon Work GroupMaturity Level: N/AStandards Status: Informative

Custom FHIR operations allow a pre-defined set of data to be passed into a function call and a pre-defined set of data to be passed back. They can be used to exchange information in two ways:

Either type of operation can be invoked in either synchronous or asynchronous mode.

Retrieval operations allow a data consumer to request information from a data source. They can be used to specify filters not possible with any of the 'standard' FHIR search languages and can also generate data that does not already exist.

The arguments to an operation that solicits data from a data source could be a resource such as CommunicationRequest or Task. It could also be any arbitrary resource or set of resources if the intended behavior were to 'match' characteristics of the specified resource(s). However, more typically, data retrieval operations will just take a series of parameters that determine the data to be found or generated. The results of such an operation would commonly be a search-set Bundle, however it could be any resource or even a Parameters instance containing a single data element or an arbitrary operation-specific collection of data elements.

Operations to that provide information from a data source to a data consumer will typically have OperationDefinition.affectsState set to 'false'. However, operations that generate new data and cause that data to be persisted in the data source would be considered to affect state and set the flag to true. Such operations must be invoked using POST, as must any operations that take parameters of resources or complex types that are not expressible on a URL. Non-state-affecting searches that can be expressed via the URL can use GET.

Operations to gather data can be very narrow in scope, tuned to ask a very specific question, or they can be very broad allowing a wide variety of parameters or even strings expressing queries in some formal language to be executed. Narrow definitions mean that each new type of data will require a new query - and custom code written for all applications that support it. Broad definitions mean that the bar is relatively high to declare support for the operation - as there is no good way in FHIR to declare 'partial' support for an operation.

Process operations involve a data source invoking an operation on a data consumer passing a set of information for the consumer to 'process'. I.e. consume the information, forward the information or otherwise do useful things with it. The specific expectations are defined as part of the description of the operation. The payload of the operation might be a single resource, a Bundle of resources or a Parameters instance with a collection of named parameters which might be resources, individual data elements or a mixture of them. Such operations will generally have the OperationDefinition.affectsState set to 'true' as processing the same information more than once could potentially result in duplicate storage or delivery of data. However, in some cases, repeated invocation might not be problematic and affectsState could be 'false'.

Because the server processing the operation will need to understand *how* to process the data, these types of operations will tend to be specific rather than generic. Also, because there's no expectation for a response to the operation other than an HTTP 200 indicating successful receipt of the data (or alternatively an error code potentially accompanied by an OperationOutcome), these operations will typically be synchronous.

Synchronous operations are performed in 'real time', with the body of the HTTP response containing the result of invoking the operation. Typically, this means a response time in the milliseconds, or at most a few seconds though HTTP timeouts can extend to a minute or longer if the use-case will tolerate that sort of duration.

Data ConsumerData Source1a. Request data (POST/GET)alt[Successful invocation]1b. RESTful response containing desired data[Failure]1c. OperationOutcome indicating failure reason

The operation is invoked by invoking a GET on the operation URL passing parameters in the URL, or by invoking a POST on the operation URL passing either a single resource in the body or a Parameters instance containing various parameters in the body. The HTTP response could contain a Bundle (typically a search response Bundle), a single resource or a Parameters instance as defined by the operation. On failure, it would generally contain an OperationOutcome.

  • Instead of having the operation always return the data, it is also possible to have more sophisticated operations where the operation response just provides pointers to some/all of it. The data would then be retrieved by a separate query process. This might be appropriate if the data source is performing an indexing function and the data is stored elsewhere, or if the identified data would be too large to reasonably return in a single synchronous response.
  • It is possible for one of the operations to have a parameter that acts like a 'paging' mechanism, such that multiple calls to the operation that increment the paging parameter (or that pass in a 'next page' value provided as part of the previous response) could allow paging through a large result set. This capability would need to be built into the definition of the operation. (And support for it would need to be custom coded in all software implementing the operation.)

Asynchronous operations are used when there is no expectation that the response will be processed within the HTTP timeout period of the server (or at least within the period the client system is willing to block for). It allows an operation to be initiated and the results of the operation to subsequently received by polling a temporary location established at the time of operation invocation. Operations can be invoked asynchronously whether using POST or GET. Details on the asynchronous process are found in the FHIR specification here

Data ConsumerData Source1a.Request data (POST/GET) indicating desired info with Prefer=respond-async1b.HTTP 202 Accepted w/ [polling location]loop2a.GET [polling location]2b.202 Accepted (w/ optional X-Progress header)3a.GET [polling location]3b.200 Ok w/ Json Response object listing [output file locations]4a.GET [output file location]4b.[output file]

Step 1: The invocation of the operation asynchronously is identical to synchronous operation 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 operation 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 operation 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, giving it the data desired.