FHIR RESTful API
Note
The features described on this page are available in all Firely Server editions.
Firely Server supports most of the features in the FHIR RESTful API.
FHIR Versions
All the operations below can be called for FHIR STU3, FHIR R4 or FHIR R5. Firely Server supports the fhirVersion mimetype parameter and fhir version endpoint mappings for that purpose. See Multiple versions of FHIR for more information.
Create, read, update, patch, delete
These five operations to manage the contents of the Firely Server, commonly referenced by the acronym CRUD, are implemented as per the specification. Patch is implemented as FHIR Patch, as this is the most versatile one. A few limitations apply.
Firely Server enables create-on-update: If you request an update and no resource exists for the given id, the provided resource will be created under the provided id. It is possible to disable this feature by setting AllowCreateOnUpdate to false in the FhirCapabilities configuration.
Firely Server can reject a resource based on Validating FHIR resources.
A conditional update whose search criteria match no resource, but whose payload contains an id, is handled as follows:
If no resource exists with that id, the resource is created under that id. With
AllowCreateOnUpdateset tofalse, Firely Server returnsHTTP 404 Not Foundinstead.If a resource with that id exists but has been deleted, the resource is brought back to life: the update is stored as a new version under that id, and Firely Server returns
HTTP 200 OK.If a resource with that id exists, is not deleted and does not match the search criteria, or exists in another FHIR version, the update is rejected with
HTTP 409 Conflict.
Configuration
A conditional delete interaction may match multiple resources. You can configure the server to delete all matches, or reject the operation (effectively only allowing single matches to be deleted). Allowing multiple deletes requires support for transactions on the database (MongoDb, SQL Server or SQLite). If you allow for multiple deletes, you have to specify a maximum number of resources that can be deleted at once, to save you from accidentally deleting too many resources.
"FhirCapabilities": {
"AllowCreateOnUpdate": true|false
"ConditionalDeleteOptions": {
"ConditionalDeleteType": "Single", // Single or Multiple,
"ConditionalDeleteMaxItems": 1
}
}
Versioned update with If-Match
An update, a conditional update or a patch can be made conditional on the current version of the resource, as described in Managing Resource Contention.
Send the version that you expect to be current in the If-Match header, in the weak ETag format that Firely Server returns in the ETag header:
If-Match: W/"<versionId>"
In a batch or transaction, use entry.request.ifMatch for the same purpose, see Conditional headers.
If the version in
If-Matchis the current version of the resource, the update is performed.If it is not, Firely Server rejects the request with
HTTP 409 Conflict. Note that the FHIR specification asks for412 Precondition Failedhere.If no resource exists with the given id, Firely Server also returns
409 Conflict, and does not create the resource, even when create-on-update is enabled.A value that is not in the weak ETag format
W/"<versionId>"never matches, so the request is rejected with409 Conflict.
Conditional headers
Firely Server supports these conditional request headers. In a batch or transaction, each entry can carry them in its request element, with the same effect:
HTTP header |
|
Effect |
|---|---|---|
|
|
Makes a create conditional. The value is a search query without the resource type, for example |
|
|
Makes an update, a conditional update or a patch conditional on the current version of the resource, see Versioned update with If-Match. |
|
|
Makes a read conditional. The value is one or more ETags, separated by commas, such as |
|
|
Makes a read conditional. The value is an HTTP date, such as |
Response headers
When a response contains a single resource, for example the response to a read, create, update or patch, Firely Server includes these headers:
ETag: the version of the resource, as a weak ETag:W/"<versionId>". Use this value in theIf-Matchheader of a versioned update.Last-Modified: themeta.lastUpdatedof the resource, as an HTTP date.
These headers are also sent when the resource is not returned in the body because the request has a Prefer: return=minimal or Prefer: return=OperationOutcome header.
After a create, an update or a conditional create or update, the Location header holds the absolute url of the new version of the resource: <firely-server-endpoint>/<resourcetype>/<id>/_history/<versionId>.
Limitations on CRUD
Simultaneous conditional creates and updates are not entirely transactional safe:
Two conditional updates may both result in a
create, although the result of one may be a match to the other.Two conditional creates may both succeed, although the result of one may be a match to the other.
A conditional create and a simultaneous conditional update may both result in a
create, although the result of one may be a match to the other.
Parameter
_prettyis not yet supported.XML Patch and JSON Patch, as well as version-read and conditional variations of FHIR Patch are not yet supported.
Update with no changes
Updating Firely Server with a resource that is identical to an existing resource in the database will normally create a new version of that resource, along with new AuditEvent and Provenance resources if auditing is enabled. This can put extra load on the server where this is not entirely necessary. To avoid these updates that can unnecessarily increase server load and database growth, the No-Op (No Operation) plugins can be enabled. By enabling these plugins, the server acknowledges the request without making any actual modifications to the database. If an update resulted in a No-Op scenario, this can be observed in the OperationOutcome that is returned by Firely Server.
Configuration for No-Op
To make sure Firely server uses the No-Op scenario, the UpdateNoOp plugins need to be enabled in the PipelineOptions.
"PipelineOptions": {
"PluginDirectory": "./plugins",
"Branches": [
{
"Path": "/",
"Include": [
"Vonk.Plugin.UpdateNoOp.UpdateNoOpConfiguration",
"Vonk.Plugin.UpdateNoOp.PatchNoOpConfiguration",
"Vonk.Plugin.UpdateNoOp.ConditionalUpdateNoOpConfiguration",
]
}
]
"UpdateNoOp": {
"EnableForHttp": true,
"EnableForPubSub": true,
"AdditionalMetaToBeIgnored": [
"security",
"tag",
"profile"
]
}
There are three No-Op plugins available:
Vonk.Plugin.UpdateNoOp.UpdateNoOpConfiguration- For regular updatesVonk.Plugin.UpdateNoOp.PatchNoOpConfiguration- For Patch operationsVonk.Plugin.UpdateNoOp.ConditionalUpdateNoOpConfiguration- For conditional updates
By default the following meta elements are ignored during resource comparison: versionId, lastUpdated and source. You can also add security, tag and profile or any other meta element to be ignored, but it depends on your specific usage of meta. For more information see the hl7 specification.
The EnableForHttp setting (default: true) controls whether the No-Op behaviour applies to regular HTTP update requests. The EnableForPubSub setting (default: true) controls whether it applies to Update and Upsert commands received via the PubSub subscriber (Vonk.Plugin.PubSub.Sub). Both can be set to false independently to disable No-Op for that channel while keeping it active for the other.
To determine if your action resulted in a No-Op scenario, you can configure Firely Server to return an OperationOutcome. For this it is necessary to configure the Prefer Header as Firely Server does not return this response by default. The Prefer Header can be set in three ways, as per the hl7 specification:
return=minimal- Nothing is returned by the serverreturn=representation- The resource is returned as present in the databasereturn=OperationOutcome- Return an OperationOutcome
In the example below an OperationOutcome for a No-Op scenario is returned when the Prefer Header is set to return=OperationOutcome:
{
"resourceType": "OperationOutcome",
"id": "26a724d9-10e4-4a71-819e-15d52f6f821c",
"meta": {
"versionId": "b6063533-a93e-4cd1-bb0b-5f37381d0f20",
"lastUpdated": "2024-02-12T11:12:40.6172822+00:00"
},
"issue": [
{
"severity": "information",
"code": "informational",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/dotnet-api-operation-outcome",
"code": "5025"
}
],
"text": "No changes were performed as the provided resource contains no changes to the existing resource"
}
},
{
"severity": "information",
"code": "informational",
"diagnostics": "The operation was successful"
}
]
}
Versioning
Firely Server keeps a full version history of every resource, including the resources on the Firely Server Administration API. When SMART on FHIR is enabled, history and version reads are only available if SmartAuthorizationOptions:AllowHistory is true, and then with restrictions. See History and version reads.
Search
Search is supported as per the specification, with a few Limitations on search.
In the default configuration the SearchParameters from the FHIR specification are available. But Firely Server also allows Custom Search Parameters.
Chaining and reverse chaining is fully supported.
Besides GET, you can search with POST on <firely-server-endpoint>/<resourcetype>/_search or <firely-server-endpoint>/_search. Send the search parameters in the body with Content-Type application/x-www-form-urlencoded (or multipart/form-data). Parameters in the query of the url are added to the ones in the body. A compartment search can only be done with GET.
The _list parameter is supported for a List resource that is stored in Firely Server: <resourcetype>?_list=<id> returns the resources of that type that are referenced in List.entry.item of the List with that id. Functional lists such as $current-allergies are not supported.
Firely Server maintains the search index only for the current version of a resource. If your data contains versioned references (e.g. Observation.subject references Patient/123/_history/2), the search may match the current version of the referenced resource (e.g. Patient/123/_history/5), even if the reference is versioned.
You can have a warning about this reported in the OperationOutcome of the search response by setting WarnOnVersionMismatches to true in the FhirCapabilities:SearchOptions configuration.
Be aware though that this has a serious performance impact. Hence this warning is disabled by default.
Quantity search on UCUM quantities automatically converts units to a canonical form. This means you can have kg in an Observation and search by lbs, or vice versa.
Compartment Search is supported.
Warning
Queries that request resource types not included in the current compartment’s CompartmentDefinition will yield default search results. Example: Searching for Practitioner resources within a Patient’s compartment will return all Practitioner resources, including the ones not linked to the patient.
Firely Server also supports _include:iterate and _revinclude:iterate, as well as its STU3 counterparts _include:recurse and _revinclude:recurse. See the specification for the definition of those. You can configure the maximum level of recursion:
"FhirCapabilities": {
"SearchOptions": {
"MaximumIncludeIterationDepth": 1
}
},
The number of resources that one _include or _revinclude adds to a search result page is limited to BundleOptions:MaxCount per included resource type, see Navigational paging links. When that limit is reached, the other matching resources are not included, and the OperationOutcome of the search response contains a warning that the limit has been reached.
_include follows a versioned reference (e.g. Patient/123/_history/2) to the referenced version of the resource. When SMART on FHIR restricts the request and SmartAuthorizationOptions:AllowHistory is false, Firely Server does not return historical versions: it includes the referenced resource only if the referenced version is its current version.
Search Anonymization
The next, previous, and last link may contain privacy-sensitive information as part of a search parameter value. In order to not expose these values in logs, the Vonk.Plugin.SearchAnonymization plugin can be used. It will replace the query parameter part of the navigational link with an opaque UUID. The plugin must be used starting with FHIR R5 as the specification mandates the removal of sensitive information.
The following settings manage the search anonymization:
"FhirCapabilities": {
"SearchOptions": {
"AnonymizationSettings": { // when the SearchAnonymization plugin is loaded, these settings will be used
"PagingLifetime": 10,// in minutes, the time an anonymized link is valid
"FallbackToEmbeddedEncryptionKey" : true, // if true and there are no keys provided or the last provided key is no longer valid, the server will use the embedded key to encrypt.
"EncryptionKeys": [ // a JWKS to use for paging encryption
{
"kty": "oct", // all keys should be of type oct
"k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0", // only base64url encoded keys are supported
"exp": "2099-12-31" // the expiration date of the key, this is a custom property and has to be added. Time will be ignored
}
]
}
}
},
The PagingLifetime will set how long the navigational links are valid in minutes. When the link is expired, a HTTP 410 Gone will be returned.
The FallbackToEmbeddedEncryptionKey setting indicates wether to use the default encryption key or not. This also means that when encryption keys are provided and this is set to true, the default key will be used when the last provided key is expired.
The EncryptionKeys setting holds a set of JWK keys. The kty should be oct and a valid k value should be generated. Each key should also have the custom exp property which states a date when this key will expire.
Modifiers
Modifiers can influence the behaviour of a search parameter. Modifiers are defined per search parameter type in the FHIR core specification. Firely Server supports modifiers for the following data types:
Search parameter types |
Modifier name |
Supported? |
|---|---|---|
All search parameter types |
:missing |
✅ |
string |
:exact |
✅ |
string |
:contains |
✅ |
token |
:text |
✅ |
token |
:not |
✅ |
token |
:of-type |
✅ |
token |
:in |
❌ |
token |
:below |
❌ |
token |
:above |
❌ |
token |
:not-in |
❌ |
reference |
:[type] |
✅ |
reference |
:identifier |
✅ |
reference |
:above |
❌ |
reference |
:below |
✅ |
uri |
:below |
✅ |
uri |
:above |
❌ |
Reference :below is supported for canonical references with a version: Firely Server matches the references to that canonical whose version starts with the given version. For example, ?derived-from:below=http://example.org/fhir/StructureDefinition/my-profile|1.2 matches the versions 1.2 and 1.2.3. The comparison is on the text of the version, so |1 also matches 10.0.
When searching with the :exact modifier the server handles grapheme clusters.
Sorting
_sort is implemented for searchparameters of types:
string
number
uri
reference
datetime
token
for the all supported repositories.
How is sort evaluated?
A searchparameter may be indexed with multiple values for a single resource. E.g. Patient.name for Angelina Jolie would have name=Angelina and name=Jolie. And George Clooney: name=George and name=Clooney. As the FHIR Specification phrases it: “In this case, the sort is based on the item in the set of multiple parameters that comes earliest in the specified sort order when ordering the returned resources.” Here is an example of how Firely Server evaluates this.
In ascending order:
Patient?_sort=nameName values
Asc. per resource
Asc. resources
Angelina
Angelina
Angelina Jolie
Jolie
Jolie
George
Clooney
George Clooney
Clooney
George
Now in descending order:
Patient?_sort=-nameName values
Desc. per resource
Desc. resources
Angelina
Jolie
Angelina Jolie
Jolie
Angelina
George
George
George Clooney
Clooney
Clooney
The searchparameter to sort on may not be indexed at all for some of the resources in the resultset. E.g. a Patient without any identifier will not be indexed for Patient.identifier. Resources not having that parameter always end up last (both in ascending and descending order). This is similar to the ‘nulls last’ option in some SQL languages.
Token parameters are sorted only on their code element. The system element is ignored in the sorting.
Firely Server uses the default collation as configured on the database server. This collation defines the ordering of characters.
All elements of type
dateandPeriodare treated as being aPeriodfor sorting. When sorting ascending, thestartof the period will be used. Similarly, when sorting descending theendof the period will be used. When sorting on a search parameter that references multipledateand/orPeriodvalues, the minimum (for ascending) or maximum (for descending) of the combined values will be used.Sorting on
_scoreis not supported.
Limitations on search
The following parameters and options are not yet supported:
_text_content_query_containedType_filterLocation.near(geo matching is not supported):approxmodifier on a quantity SearchParameter:textmodifier on a string SearchParameter:above,:below,:inand:not-inmodifiers on a token SearchParameter,aboveandbeloware also not supported for Mime Types.:abovemodifier on a reference SearchParameter, and:belowon a reference that is not a canonical (only valid on a strict hierarchy)_revincludewill match the current version of the referenced resources, also if the reference is versioned._prettyImplicit ranges are supported on dates, datetimes and quantities with a UCUM unit. But not on other quantities and number parameters.
Search parameter arguments in exponential form (e.g. 1.8e2).
_total=estimate. Onlynoneandaccurateare supported. A request with_total=estimateis not rejected, but Firely Server does not calculate a quick estimate: it counts the matches as foraccurate.
In addition, Firely Server does not support the search parameters whose field xpathUsage (STU3, R4) or processingMode (R5) is not set to normal. Concretely, this means that the following search parameters are not supported:
http://hl7.org/fhir/SearchParameter/individual-phonetic(STU3, R4, R5).http://hl7.org/fhir/SearchParameter/InsurancePlan-phonetic(R4, R5)http://hl7.org/fhir/SearchParameter/Location-near(STU3, R4, R5),http://hl7.org/fhir/SearchParameter/Location-near-distance(STU3),http://hl7.org/fhir/SearchParameter/Organization-phonetic(STU3, R4, R5),http://hl7.org/fhir/SearchParameter/Resource-in(R5),
Furthermore:
Paging is supported, but it is not isolated from intermediate changes to resources.
Warning
US-Core search parameters interfere with the evaluation of composite search parameters in Firely Server. US-Core redefines the Observation.code parameter, but does not redefine the related composite search parameters.
If you load the artifacts of US-Core into the administration endpoint, be aware that you need updated versions of the composite search parameters as well.
The pre-built SQLite administration database, that comes with the Firely Server distribution, has US-Core 3.1.1 preloaded. In this database, Firely has already taken care of this for you.
Corrected versions of the search parameters are:
Observation.code-value-concept:
downloadObservation.code-value-date:
downloadObservation.code-value-quantity:
downloadObservation.code-value-string:
download
You can add these as individual files to your administration import folder, or merge them into the US-Core package.
History
History is supported as described in the specification, on the system, type and instance level.
The _since and _count parameters are also supported.
The response will be a Bundle which adheres to the BundleOptions configuration, see Search size.
Limitations on history
_atparameter is not yet supported.Paging is supported, but it is not isolated from intermediate changes to resources.
When SMART on FHIR is enabled,
_historycan only be used ifSmartAuthorizationOptions:AllowHistoryistrue, and then with restrictions. See History and version reads.With virtual multi-tenancy enabled,
_historyandvreadcannot be used.
Batch
Batch is fully supported on the usual endpoint. You can limit the number of entries accepted in a single batch. See Protect against large input.
Note that batches are not supported in the /administration endpoint.
Transaction
Transactions are supported, but with the following limitation:
The
/administrationendpoint does not support transactions.
You can limit the number of entries accepted in a single transaction. See Protect against large input.
Capabilities
On the Capabilities interaction (<firely-server-endpoint>/metadata) Firely Server returns a CapabilityStatement that is built dynamically from the
supported ResourceTypes, SearchParameters and interactions. E.g. if you Configure Search Parameters, the SearchParameters that are actually loaded appear in the CapabilityStatement.
CapabilityStatement.instantiates lists the CapabilityStatements that Firely Server conforms to. Firely Server fills it with the canonical url (url|version if the CapabilityStatement has a version) of every CapabilityStatement in the administration database that has mode set to server and status set to active, up to BundleOptions:MaxCount of them.
To declare that Firely Server instantiates the server CapabilityStatement of an Implementation Guide, load that CapabilityStatement into the administration database, for example as part of the IG package.
Not supported interactions
These interactions are not yet supported by Firely Server:
HEAD
Besides that, Firely Server does not yet return the date header as specified in HTTP return values