Jump To...
RESTful Clients and Servers both share the same method pattern, with one key difference: A client is defined using annotated methods on an interface which are used to retrieve resources, whereas a server requires concrete method implementations to actually provide those resources.
Unless otherwise specified, the examples below show server implementations, but client methods will follow the same patterns.
The following table lists the operations supported by HAPI FHIR RESTful Servers and Clients.
Operation | Definition |
Instance - Read | Read the current state of the resource |
Instance - VRead | Read the state of a specific version of the resource |
Instance - Update | Read the state of a specific version of the resource |
Instance - Delete | Delete a resource |
Instance - History | Retrieve the update history for a particular resource |
Type - Create | Create a new resource with a server assigned id |
Type - Search
|
Search the resource type based on some filter criteria |
Type - History | Retrieve the update history for a particular resource type |
Type - Validate | Check that the content would be acceptable as an update |
System - Conformance | Get a conformance statement for the system |
System - Transaction | Update, create or delete a set of resources as a single transaction |
System - History | Retrieve the update history for all resources |
System - Search | Search across all resource types based on some filter criteria |
Tag Operations | Search across all resource types based on some filter criteria |
The read operation retrieves a resource by ID. It is annotated with the @Read annotation, and has a single parameter annotated with the @IdParam annotation.
Example URL to invoke this method:
http://fhir.example.com/Patient/111
The following snippet shows how to define a client interface to handle a read method.
The vread operation retrieves a specific version of a resource with a given ID. To support vread, simply add "version=true" to your @Read annotation. This means that the read method will support both "Read" and "VRead". The IdDt may or may not have the version populated depending on the client request.
Example URL to invoke this method:
http://fhir.example.com/Patient/111/_history/2
The update operation updates a specific resource instance (using its ID), and optionally accepts a version ID as well (which can be used to detect version conflicts).
Update methods must be annotated with the @Update annotation, and have a parameter annotated with the @Resource annotation. This parameter contains the resource instance to be created.
In addition, the method must have a parameter annotated with the @IdParam annotation, and optionally may have a parameter annotated with the @VersionIdParam
Update methods must return an object of type MethodOutcome . This object contains the identity of the created resource.
The following snippet shows how to define an update method on a server:
Example URL to invoke this method (this would be invoked using an HTTP PUT,
with the resource in the PUT body):
http://fhir.example.com/Patient
The following snippet shows how the corresponding client interface would look:
In the case of a server which is able to do version conflict checking, an extra parameter would be added:
The delete operation retrieves a specific version of a resource with a given ID. It takes a single ID parameter annotated with an @IdParam annotation, which supplies the ID of the resource to delete.
Delete methods are allowed to return the following types:
void
, in which case
the server will return an empty response and the client will ignore
any successful response from the server (failure responses will still throw
an exception)
MethodOutcome
,
which is a wrapper for the FHIR OperationOutcome resource, which may optionally be returned
by the server according to the FHIR specification.
Example URL to invoke this method (HTTP DELETE):
http://fhir.example.com/Patient/111
The create operation saves a new resource to the server, allowing the server to give that resource an ID and version ID.
Create methods must be annotated with the @Create annotation, and have a single parameter annotated with the @Resource annotation. This parameter contains the resource instance to be created.
Create methods must return an object of type MethodOutcome . This object contains the identity of the created resource.
The following snippet shows how to define a server create method:
Example URL to invoke this method (this would be invoked using an HTTP POST,
with the resource in the POST body):
http://fhir.example.com/Patient
The following snippet shows how the corresponding client interface would look:
The search operation returns a bundle with zero-to-many resources of a given type, matching a given set of parameters.
The following example shows a search with no parameters. This operation should return all resources of a given type (this obviously doesn't make sense in all contexts, but does for some resource types).
Example URL to invoke this method:
http://fhir.example.com/Patient
To allow a search using given search parameters, add one or more parameters to your search method and tag these parameters as either @RequiredParam or @OptionalParam .
This annotation takes a "name" parameter which specifies the parameter's name (as it will appear in the search URL). FHIR defines standardized parameter names for each resource, and these are available as constants on the individual HAPI resource classes.
Parameters which take a string as their format should use the StringParameter type. They may also use normal java Strings (or HAPI's StringDt), although it is not possible to use the ":exact" qualifier in that case.
Example URL to invoke this method:
http://fhir.example.com/Patient?family=SMITH
Searches method parameters may be of any type that implements the IQueryParameterType interface.
The "token" type is used for parameters which have two parts, such as an idnetifier (which has a system URI, as well as the actual identifier) or a code (which has a code system, as well as the actual code). For example, the search below can be used to search by identifier (e.g. search for an MRN).
Example URL to invoke this method:
http://fhir.example.com/Patient?identifier=urn:foo|7000135
The FHIR specification provides a sytax for specifying dates (and date/times as well, but for simplicity we will just say dates here) as search criteria.
Dates may be optionally prefixed with a qualifier. For example, the
string
>=2011-01-02
means any date on or after 2011-01-02.
To accept a qualified date parameter, use the QualifiedDateParam parameter type.
Example URL to invoke this method:
http://fhir.example.com/Observation?birthdate=>=2011-01-02
Invoking a client of thie type involves the following syntax:
A common scenario in searches is to allow searching for resources with values (i.e timestamps) within a range of dates.
FHIR allows for multiple parameters with the same key, and interprets
these as being an "AND" set. So, for example, a range of
DATE >= 2011-01-01
and
DATE < 2011-02-01
can be interpreted as any date within January 2011.
The following snippet shows how to accept such a range, and combines it with a specific identifier, which is a common scenario. (i.e. Give me a list of observations for a specific patient within a given date range)
Example URL to invoke this method:
http://fhir.example.com/Observation?subject.identifier=7000135&date=>=2011-01-01&date=<2011-02-01
Invoking a client of this type involves the following syntax:
Note that when using a date range parameter, it is also possible for the client to request an "unbounded" range. In other words, a range that only a start date and no end date, or vice versa.
An example of this might be the following URL, which refers to any Observation
resources for the given MRN and having a date after 2011-01-01.
http://fhir.example.com/Observation?subject.identifier=7000135&date=>=2011-01-01
When such a request is made of a server (or to make such a request from a client),
the
getLowerBound()
or
getUpperBound()
property of the
DateRangeParam
object will be set to
null
.
Quantity parameters allow a number with units and a comparator
The following snippet shows how to accept such a range, and combines it with a specific identifier, which is a common scenario. (i.e. Give me a list of observations for a specific patient within a given date range)
Example URL to invoke this method:
http://fhir.example.com/Observation?value-quantity=<=123.2||mg
Many search parameters refer to resource references. For instance, the Patient parameter "provider" refers to the resource marked as the managing organization for patients.
Reference parameters use the ReferenceParam type. Reference param objects being passed into server methods will have three properties populated:
Example URLs to invoke this method:
Resource by ID: http://fhir.example.com/Patient?provider=1234
Resource by chained parameter value: http://fhir.example.com/Patient?provider:Organization.name=FooOrg
Search methods may take multiple parameters, and these parameters
may (or may not) be optional.
aaaa
To add a second required parameter, annotate the
parameter with
@RequiredParam
.
To add an optional parameter (which will be passed in as
null
if no value
is supplied), annotate the method with
@OptionalParam
.
You may annotate a method with any combination of as many @RequiredParam and as many @OptionalParam parameters as you want. It is valid to have only @RequiredParam parameters, or only @OptionalParam parameters, or any combination of the two.
Example URLs to invoke this method:
http://fhir.example.com/Patient?family=SMITH
http://fhir.example.com/Patient?family=SMITH&given=JOHN
It is possible to accept multiple values of a single parameter as well. This is useful in cases when you want to return a list of resources with criteria matching a list of possible values. See the FHIR Specification for more information.
The FHIR specification allows two types of composite parameters:
?language=FR,NL
) this is treated as an
OR
relationship, and
the search should return elements matching either one or the other.
?language=FR&language=NL
) this is treated as an
AND
relationship,
and the search should return only elements matching both.
To accept a composite parameter, use a parameter type which implements the IQueryParameterOr interface. For example, to accept searches for Observations matching any of a collection of names:
Example URL to invoke this method:
http://fhir.example.com/Observation?name=urn:fakenames|123,urn:fakenames|456
To accept a composite parameter, use a parameter type which implements the IQueryParameterAnd interface.
See the section below on date ranges for one example of an AND parameter.
FHIR allows clients to request that specific linked resources be included as contained resources, which means that they will be "embedded" in a special container called "contained" within the parent resource.
HAPI allows you to add a parameter for accepting includes if you wish to support them for specific search methods.
Example URL to invoke this method:
http://fhir.example.com/DiagnosticReport?subject.identifier=7000135&_include=DiagnosticReport.subject
It is also possible to use a String type for the include parameter, which is more convenient if only a single include (or null for none) is all that is required.
FHIR supports named queries , which may have specific behaviour defined. The following example shows how to create a Search operation with a name.
This operation can only be invoked by explicitly specifying the given query name in the request URL. Note that the query does not need to take any parameters.
Example URL to invoke this method:
http://fhir.example.com/Patient?_query=namedQuery1&someparam=value
FHIR supports sorting according to a specific set of rules.
According to the specification, sorting is requested by the client using a search param as the sort key. For example, when searching Patient resources, a sort key of "given" requests the "given" search param as the sort key. That param maps to the values in the field "Patient.name.given".
Sort specifications can be passed into handler methods by adding a parameter of type SortSpec, which has been annotated with the @Sort annotation, as shown in the following example:
Example URL to invoke this method:
http://fhir.example.com/Patient?_identifier=urn:foo|123&_sort=given
The validate tests whether a resource passes business validation, and would be acceptable for saving to a server (e.g. by a create or update method).
Validate methods must be annotated with the @Validate annotation, and have a parameter annotated with the @Resource annotation. This parameter contains the resource instance to be created.
Validate methods may optionally also have a parameter oftype IdDt annotated with the @IdParam annotation. This parameter contains the resource ID (see the FHIR specification for details on how this is used)
Validate methods must return normally if the resource validates successfully, or throw an UnprocessableEntityException or InvalidRequestException if the validation fails.
Validate methods must return either:
The following snippet shows how to define a server validate method:
Example URL to invoke this method (this would be invoked using an HTTP POST,
with the resource in the POST body):
http://fhir.example.com/Patient/_validate
FHIR defines that a FHIR Server must be able to export a conformance statement, which is an instance of the Conformance resource describing the server itself.
The HAPI FHIR RESTful server will automatically export such a conformance statement. See the RESTful Server documentation for more information.
If you wish to override this default behaviour by creating your own metadata provider, you simply need to define a class with a method annotated using the @Metadata annotation.
To create a Client which can retrieve a Server's conformance statement is simple. First, define your Client Interface, using the @Metadata annotation:
Then use the standard RESTful Client mechanism for instantiating a client:
The transaction action is among the most challenging parts of the FHIR specification to implement. It allows the user to submit a bundle containing a number of resources to be created/updated/deleted as a single atomic transaction.
HAPI provides a skeleton for implementing this action, although most of the effort will depend on the underlying implementation. The following example shows how to define a transaction method.
Example URL to invoke this method:
POST http://fhir.example.com/
(note that the content of this POST will be a bundle)
Not yet implemented - Get in touch if you would like to help!
The history operation retrieves a historical collection of all versions of a single resource (instance history) , all resources of a given type (type history) , or all resources of any type on a server (server history) .
History methods must be annotated with the @History annotation, and will have additional requirements depending on the kind of history method intended:
type()
value in the @History annotation if it is
defined in a
plain provider
.
type()
value in the @History annotation if it is
defined in a
plain provider
.
type()
value specified in
the @History annotation.
The following snippet shows how to define a history method on a server:
The following snippet shows how to define various history methods in a client.
When implementing a server operation, there are a number of failure conditions specified. For example, an Instance Read request might specify an unknown resource ID, or a Type Create request might contain an invalid resource which can not be created.
In these cases, an appropriate exception should be thrown. The HAPI RESTful API includes a set of exceptions extending BaseServerResponseException which represent specific HTTP failure codes.
See the Exceptions List for a complete list of these exceptions. Note that these exceptions are all unchecked exceptions, so they do not need to ne explicitly declared in the method signature.
FHIR RESTful servers may support a feature known as tagging. Tags are a set of named flags called "terms" (with an optional accompanying human friendly name called a "label", and an optional namespace called a "scheme").
Tags have very specific semantics, which may not be obvious simply by using the HAPI API. It is important to review the specification pages here and here before attempting to implement tagging in your own applications.
Tags are stored within a resource object, in the IResource.html#getResourceMetadata() map, under the key TAG_LIST.
In a server implementation, you may populate your tags into the returned resource(s) and HAPI will automatically place these tags into the response headers (for read/vread) or the bundle category tags (for search). The following example illustrates how to return tags from a server method. This example shows how to supply tags in a read method, but the same approach applies to vread and search operations as well.
In a client operation, you simply call the read/vread/search method as you normally would (as described above), and if any tags have been returned by the server, these may be accessed from the resource metadata.
Within a Type Create or Instance Update method, it is possible for the client to specify a set of tags to be stored along with the saved resource instance.
Note that FHIR specifies that in an update method, any tags supplied by the client are copied to the newly saved version, as well as any tags the existing version had.
To work with tags in a create/update method, the pattern used in the read examples above is simply revered. In a server, the resource which is passed in will be populated with any tags that the client supplied:
FHIR also provides a number of operations to interact directly with tags. These methods may be used to retrieve lists of tags that are available on the server, or to add or remove tags from resources without interacting directly with those resources.
On a server these methods may be placed in a plain provider, or in a resource provider in the case of resource type specific methods.
On a client, the methods are defined in the exact same way, except that there is no method body in the client interface.