This section outlines how to use the FHIR Endpoint to perform basic CRUD (Create/Read/Update/Delete) Operations.
All of the examples on this page can be performed on your own computer, pointing at a demonstration instance of Smile CDR, or at your own self-hosted instance.
Because FHIR is based on the HTTP protocol, you can do some testing right in a browser. The search and read operations are particularly well suited for this. However, if you want to try other features out, you may need a more advanced testing tool. For example, FHIR create
operations require you to use an HTTP POST
operation. Secured endpoints may require you to add special Authorization headers.
For many of the examples below, a sample curl command is shown. curl is a tool which can be used to execute HTTP requests from the command line, and is commonly installed on Linux/OSX systems.
A friendlier way of testing is to use Postman (website: http://getpostman.com. Postman is a free GUI-based testing UI for executing REST requests. It can be installed as a standalone application, or as a Chrome App.
Executing a RESTful FHIR call using Postman is fairly straightforward, as shown in the screenshot below.
POST
or PUT
or whatever the operation calls forraw
JSON
Send
A client may create a new resource on a FHIR server by performing a create operation. The create uses an HTTP POST
against the URL [baseUrl]/[resourceType]
. This POST should have a Content-Type
header which specifies the encoding of the payload.
The following example shows a simple create using a JSON Payload.
HTTP Method: POST
Address:
http://localhost:8000/Patient
Headers:
Content-Type: application/fhir+json
Request Body:
{
"resourceType": "Patient",
"identifier": [ { "system": "urn:oid:1.2.36.146.595.217.0.1", "value": "12345" } ],
"name": [ {
"family": "Chalmers",
"given": [ "Peter", "James" ]
} ],
"gender": "male",
"birthDate": "1974-12-25"
}
The FHIR endpoint will respond with a response similar to the following:
HTTP/1.1 201 Created
ETag: W/"1"
Location: http://localhost:8000/Patient/123/_history/1
{
"resourceType": "Patient",
"id": "123",
"meta": {
"versionId": "1",
"lastUpdated": "2019-07-12T01:43:59.381+00:00"
},
"identifier": [
{
"system": "urn:oid:1.2.36.146.595.217.0.1",
"value": "12345"
}
],
"name": [
{
"family": "Chalmers",
"given": [
"Peter",
"James"
]
}
],
"gender": "male",
"birthDate": "1974-12-25"
}
Note the Location
header, which indicates the ID that was assigned to this resource by Smile CDR when the resource was saved in the database. In this case, the assigned ID was 123
. Also note the version assigned to the newly created resource, which was 1
since this is the first version.
A variant on the regular create called Conditional Create can be used in cases where you want to create a resource only if one doesn't already exist matching a given expression. For example, a common use case is that you know a Patient's identifier (Patient.identifier) and want to ensure that a Patient resource exists matching this identifier value so that other resources can link to it. In this case, an HTTP POST is still used but an additional header called If-None-Exist
is added to the request. The value of this header is a FHIR search expression, containing only the portion after the ?
character. The server will attempt to resolve the search expression.
Conditional creates are especially useful as a part of FHIR transactions. See Conditional Create on the Transactions page for more information.
For example, the following could be used to create a resource with the identifier http://example.org|12345
.
HTTP Method: POST
Address:
http://localhost:8000/Patient
Headers:
If-None-Exist: identifier=http://example.org|12345
Content-Type: application/fhir+json; charset=UTF-8
The server will resolve the search specified in the If-None-Exist
header, and:
Location
header.Location
header.If you know the ID of a resource on a FHIR server, you can read back the most recent version of that resource by performing a read operation. The read uses an HTTP GET
against the URL [baseUrl]/[resourceType]/[id]
.
The following example shows a simple read of the resource created and updated in the examples above.
HTTP Method: GET
Address:
http://localhost:8000/Patient/123
The FHIR endpoint will respond with a response similar to the following:
HTTP/1.1 200 OK
ETag: W/"1"
Location: http://localhost:8000/Patient/123/_history/1
{
"resourceType": "Patient",
"id": "123",
"meta": {
"versionId": "3",
"lastUpdated": "2019-07-12T01:58:07.164+00:00"
},
"identifier": [
{
"system": "urn:oid:1.2.36.146.595.217.0.1",
"value": "12345"
}
],
"name": [
{
"family": "Chalmers",
"given": [
"Peter",
"James"
]
}
],
"gender": "male",
"birthDate": "1974-02-13",
"address": [
{
"line": [
"534 Erewhon St"
],
"city": "PleasantVille",
"state": "Vic",
"postalCode": "M5C 2X8"
}
]
}
You can also include a version string in the URL in order to request a specific version of the resource by performing a vread operation. The vread uses an HTTP GET
against the URL [baseUrl]/[resourceType]/[id]/_history/[versionId]
.
The following example shows a simple vread of the original resource created in the first example above.
HTTP Method: GET
Address:
http://localhost:8000/Patient/123/_history/1
You can update an existing resource on a FHIR server by performing an update operation. The update uses an HTTP PUT
against the URL [baseUrl]/[resourceType]/[id]
. This PUT should have (at a minimum) a Content-Type
header that specifies the encoding of the payload.
The following example shows a simple update using a JSON payload.
In this example, we are taking the previously created resource from above and updating it. We will change the birth date and add an address. Note the ID 123
in the URL, resource body and headers. When you are trying this yourself, make sure to use the ID that was returned from the server in the steps above.
HTTP Method: PUT
Address:
http://localhost:8000/Patient/123
Headers:
Content-Type: application/fhir+json
Request Body:
{
"resourceType": "Patient",
"id": "123",
"identifier": [ { "system": "urn:oid:1.2.36.146.595.217.0.1", "value": "12345" } ],
"name": [ {
"family": "Chalmers",
"given": [ "Peter", "James" ]
} ],
"gender": "male",
"birthDate": "1974-01-13",
"address": [ {
"line": [ "534 Erewhon St" ],
"city": "PleasantVille",
"state": "Vic",
"postalCode": "3999"
} ]
}
The FHIR endpoint will respond with a response similar to the following:
HTTP/1.1 200 OK
ETag: W/"2"
Location: http://localhost:8000/Patient/123/_history/2
{
"resourceType": "Patient",
"id": "123",
"meta": {
"versionId": "2",
"lastUpdated": "2019-07-12T01:46:17.950+00:00"
},
"identifier": [
{
"system": "urn:oid:1.2.36.146.595.217.0.1",
"value": "12345"
}
],
"name": [
{
"family": "Chalmers",
"given": [
"Peter",
"James"
]
}
],
"gender": "male",
"birthDate": "1974-01-13",
"address": [
{
"line": [
"534 Erewhon St"
],
"city": "PleasantVille",
"state": "Vic",
"postalCode": "3999"
}
]
}
Updating a resource with the same content that is already present on a server will produce a "No-Op" outcome. The server will respond successfully, but no changes will actually be stored.
If you want to know whether your operation was a No-Op or not, you can examine the OperationOutcome resource that is returned by the server. You may need to set the Prefer Header in order to instruct the server to return an outcome.
A variant on the regular update called Conditional Update can be used in cases where you want to update a resource but do not know its resource ID. In this case, an HTTP PUT is still used but the URL is a FHIR search expression. The server will attempt to resolve the search expression and update the resource which matches it.
Conditional updates are especially useful as a part of FHIR transactions. See Conditional Update on the Transactions page for more information.
For example, the following could be used to update a resource with the identifier http://example.org|12345
.
HTTP Method: PUT
Address:
http://localhost:8000/Patient
The server will resolve this search, and:
You can also include a version string in the URL in order to update a specific version of the resource by performing an Update with History Rewrite operation.
The Update with History Rewrite uses an HTTP PUT
against the URL [baseUrl]/[resourceType]/[id]/_history/[versionId]
.
The request must include the header X-Rewrite-History
, and should be set to true. The body of the request must include the resource with the same ID and version as defined in the PUT request.
You can patch a resource on a FHIR server by performing a patch operation. The patch uses an HTTP PATCH
against the URL [baseUrl]/[resourceType]/[id]
. This PATCH should have (at a minimum) a Content-Type
header that specifies the encoding of the payload.
A patch can be used to modify a resource by specifying specific changes that should be made to it without needing to specify the entire contents of the new version. Patching a resource triggers the creation of a new version of the resource, and is subject to all of the same security checks and other processing rules as a regular update.
There are several supported patch document types:
The following example shows a simple patch of the resource created and updated in the examples above.
HTTP Method: PATCH
Address:
http://localhost:8000/Patient/123
Headers:
The content type depends on the patch document format being used:
application/fhir+json
.application/json-patch+json
.application/xml-patch+xml
.Content-Type: application/fhir+json
Request Body:
{
"resourceType": "Parameters",
"parameter": [ {
"name": "operation",
"part": [ {
"name": "type",
"valueCode": "replace"
}, {
"name": "path",
"valueString": "Patient.identifier"
}, {
"name": "value",
"valueIdentifier": {
"system": "http://new-system",
"value": "0001"
}
} ]
} ]
}
The FHIR endpoint will respond with a response similar to the following:
HTTP/1.1 200 OK
ETag: W/"2"
Location: http://localhost:8000/Patient/123/_history/2
{
"resourceType": "Patient",
"id": "123",
"meta": {
"versionId": "3",
"lastUpdated": "2019-07-12T01:58:07.164+00:00"
},
"identifier": [
{
"system": "http://new-system",
"value": "0001"
}
],
"name": [
{
"family": "Chalmers",
"given": [
"Peter",
"James"
]
}
],
"gender": "male",
"birthDate": "1974-01-13",
"address": [
{
"line": [
"534 Erewhon St"
],
"city": "PleasantVille",
"state": "Vic",
"postalCode": "3999"
}
]
}
You can delete a resource on a FHIR server by performing a delete operation. The delete uses an HTTP DELETE
against the URL [baseUrl]/[resourceType]/[id]
. This operation performs a logical delete, which has a specific set of semantics:
The following example shows a simple delete of the resource created and updated in the examples above.
HTTP Method: DELETE
Address:
http://localhost:8000/Patient/123
The FHIR endpoint will respond with a response similar to the following:
HTTP/1.1 200 OK
ETag: W/"3"
Location: http://localhost:8000/Patient/123/_history/3
{
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "information",
"code": "informational",
"diagnostics": "Successfully deleted 1 resource(s) in 29ms"
}
]
}
For operations which create or update a resource (e.g. create / update / patch), by default the server will respond with a copy of the resource as it was stored.
This will typically resemble the resource you supplied in the request body, but may differ in several ways:
Resource.meta.version
number and Resource.meta.lastUpdated
timestamp will be updated to reflect the new version (unless the operation was a No-Op).Instead of responding with a complete resource payload, you can use the Prefer
request header to change the response. Acceptable values include:
Prefer: return=minimal | Do not include a response payload. This option minimizes the amount of data that is transferred and makes sense if you do not intent to inspect the response body. You can still tell whether an operation succeeded or not by examining the HTTP Status Code. |
Prefer: return=representation | Include the stored FHIR resource as it was stored. This is the default. |
Prefer: return=OperationOutcome | Return an OperationOutcome resource containing information about what was performed. The OperationOutcome will generally contain a Storage Outcome Status Code. |
When performing a write operation such as a create or an update (and especially when performing the conditional equivalents), it can be helpful to understand what the server actually did internally during the operation. For example, when you perform a conditional update you may want to know whether the system updated an existing matching resource or created a new one.
If you include a request header of Prefer: return=OperationOutcome
as described in Controlling the Response Payload, an OperationOutcome resource will be returned with both a human-readable description of the outcome, and an equivalent machine processable coding.
For example, consider a conditional update using an HTTP PUT to the URL: http://localhost:8000/Patient?active=true. The following shows an example response:
{
"resourceType": "OperationOutcome",
"issue": [ {
"severity": "information",
"code": "informational",
"details": {
"coding": [ {
"system": "https://hapifhir.io/fhir/CodeSystem/hapi-fhir-storage-response-code",
"code": "SUCCESSFUL_CREATE_WITH_CONDITIONAL_MATCH",
"display": "Conditional create succeeded: an existing resource matched the conditional URL so no action was taken."
} ]
},
"diagnostics": "Successfully conditionally created resource \"3\". Existing resource matched URL \"Patient?active=true\"."
} ]
}
The table below shows the status codes that can be included in an OperationOutcome response.
Code | Display |
---|---|
SUCCESSFUL_CREATE | Create succeeded. |
SUCCESSFUL_CREATE_NO_CONDITIONAL_MATCH | Conditional create succeeded: no existing resource matched the conditional URL. |
SUCCESSFUL_CREATE_WITH_CONDITIONAL_MATCH | Conditional create succeeded: an existing resource matched the conditional URL so no action was taken. |
SUCCESSFUL_UPDATE | Update succeeded. |
SUCCESSFUL_UPDATE_AS_CREATE | Update as create succeeded. |
SUCCESSFUL_UPDATE_NO_CHANGE | Update succeeded: No changes were detected so no action was taken. |
SUCCESSFUL_UPDATE_NO_CONDITIONAL_MATCH | Conditional update succeeded: no existing resource matched the conditional URL so a new resource was created. |
SUCCESSFUL_UPDATE_WITH_CONDITIONAL_MATCH | Conditional update succeeded: an existing resource matched the conditional URL and was updated. |
SUCCESSFUL_UPDATE_WITH_CONDITIONAL_MATCH_NO_CHANGE | Conditional update succeeded: an existing resource matched the conditional URL and was updated, but no changes were detected so no action was taken. |
SUCCESSFUL_DELETE | Delete succeeded. |
SUCCESSFUL_DELETE_ALREADY_DELETED | Delete succeeded: Resource was already deleted so no action was taken. |
SUCCESSFUL_DELETE_NOT_FOUND | Delete succeeded: No existing resource was found so no action was taken. |
SUCCESSFUL_PATCH | Patch succeeded. |
SUCCESSFUL_PATCH_NO_CHANGE | Patch succeeded: No changes were detected so no action was taken. |
SUCCESSFUL_CONDITIONAL_PATCH | Conditional patch succeeded. |
SUCCESSFUL_CONDITIONAL_PATCH_NO_CHANGE | Conditional patch succeeded: No changes were detected so no action was taken. |