As you probably know, FHIR lets you extend its datamodel using extensions, thus enabling you to add application and usecase specific data to the “core” datamodels. You can publish the details about these extensions on a FHIR server or repository, so people can find out what your extension means and what kind of data it allows.
In DSTU2 we introduced another extension mechanism: the Operations Framework. This framework allows you to add new operations to the FHIR REST interface, so you can add application-specific functionality that your clients can call. And just as with extensions, you can publish details about these operations on your FHIR server so others can find out how to invoke the operation.
But what exactly is an “operation” in the context of FHIR’s rest interface? In DSTU1 we were not particularly clear about this and we simply had a list of interactions, which include things like “read”, “search” and “validate”. In addition, DSTU1 offered a search parameter called “_query” that took a name of a custom “query”. Interestingly enough, the only “query” defined by the specification was the valueset expansion, which triggered amusing debates whether this actually qualified as a query. Moreover, we had the need to add operations that had side-effects and which took parameters of complex types. All of which was not possible with the “_query” extension point. This resulted in the addition of explicitly defined operations.
When you are invoking actions on a FHIR server, you are now either using the basic core “interactions”, which are just the CRUD operations, search and history. Anyhting else is an operation. The specification comes with certain pre-defined operations and it will let you define new ones by your own design.
Invoking an operation
Operations are (mostly) POSTs to a FHIR endpoint, where the name of the operations is prefixed by a “$” sign. For example:
Whenever the operation is idempotent it may be invoked using GET as well (as is the case with the example above).
Operations can be invoked on four types of FHIR endpoints:
- The “base” FHIR service endpoint (e.g. http://fhir.someserver.org/fhir) – these are operations that operate on the full scale of the server. For example: return me all extensions known by this server.
- A resource type (e.g. http://fhir.someserver.org/fhir/Patient) – these operations operate across all instances of the given type
- A resource instance (e.g. http://fhir.someserver.org/fhir/Patient/1) – for operations that involve a single instance, like the $everything operation above
- A version of a resource instance (http://fhir.someserver.org/fhir/Patient/1/_history/4) – which, as you can guess by now, is used by operations that involve a specific version of a specific instance of FHIR data. These are a bit exceptional in the sense that they should not alter instance data (since that would require creating an update) and were specifically introduced to allow manipulation of profile and tag metadata (which is allowed without creating a new version).
The body of the invocation contains a special infrastructure resource called Parameters (yes, plural). This Resource represents a collection of named parameters as <key,value> pairs, where the value may be any primitive or complex datatype or even a full Resource. In addition you may pass in strings that are formatted as the search parameter types.
On completion, the operation returns another Parameters resource, this time containing one or more (!) output “parameters”. This means that a FHIR operation can take any parameter “in” and return a set out result parameters “out”. We chose to introduce this special Parameters resource so both the body of the POST and the returned result are always a Resource. Had we not done this, we would have needed to expand the FHIR interface and FHIR serialization to allow for bodies that were just a complex datatype or primitive.
Publishing an operation’s definition
Invoking an operation is just the first part of the story. To make your new operations discoverable you will use the new OperationDefinition resource. Just like with extensions this is a computable expression of the definition of your operation, containing some metadata and details about the allowed parameters and result valies. Since the OperationDefinition is itself a resource, you can publish it in your FHIR server endpoint or a FHIR repository. Developer tools can take this definition and generate a strongly-typed API interface for it, and -just like our publication tool does– you may use it as source to generate documentation for your new operation.
The DSTU2 core specification comes with quite some pre-defined operations, and you will find some of the old DSTU1 interactions that your thought were missing in DSTU2, now showing up as an operation:
- Validation ($validate) – this replaces (and expands on) the validation functionality of the old _validate interaction in DSTU1. You can send in a Resource (or a reference to a resource) and a reference to a StructureDefinition to get your resource validated.
- Tag operations ($meta, $meta-add, $meta-delete) – replace the tag operations in DSTU1 to add profile, tag and security tag metadata for a resource instance.
- The Mailbox endpoint ($mailbox) – Delivers a message to a FHIR server.
In addition, the specification now has a full set of ValueSet operations, which turn a FHIR server into a terminology server, offering limited CTS capabilities.
Finally, you can find all operations defined for a Resource on the new “Operations” tab that was added to all Resource pages