5.1.1FHIR CRUD Operations

 

This section outlines how to use the FHIR Endpoint to perform basic CRUD (Create/Read/Update/Delete) Operations.

5.1.2Testing Tools

 

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.

  • Place a URL (such as http://localhost:8000 ) in the URL bar
  • Set the verb to POST or PUT or whatever the operation calls for
  • On the body tab:
  • Choose a body type of raw
  • Set the type dropdown to JSON
  • Paste in your resource body
  • Click Send

Postman

5.1.3FHIR Create

 

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.

5.1.3.1Request

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"
}

5.1.3.2Response

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.

5.1.3.3Conditional Create

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:

  • If no resources match the search, a resource will be created and the assigned ID will be returned in a Location header.
  • If a single resource matches the search, it will not be modified but its ID will be returned in a Location header.
  • If multiple resources match the search, an error will be raised and no action will be performed.

5.1.4FHIR Read / vRead

 

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.

5.1.4.1Request

HTTP Method: GET

Address: http://localhost:8000/Patient/123

5.1.4.2Response

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"
        }
    ]
}

5.1.4.3Version-Specific Read (vRead)

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].

In HAPI FHIR / Smile CDR, the version ID of a resource is an integer that starts at 1 for the first version of a resource, and increments for each update. This is not a requirement however, and other servers may use a different versioning scheme.

The following example shows a simple vread of the original resource created in the first example above.

5.1.4.4Request

HTTP Method: GET

Address: http://localhost:8000/Patient/123/_history/1

5.1.5FHIR Update

 

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.

5.1.5.1Request

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"
  } ]
}

5.1.5.2Response

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"
        }
    ]
}

5.1.5.3Update with No Changes

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.

5.1.5.4Conditional Update

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?identifier=http://example.org|12345

The server will resolve this search, and:

  • If no resources match the search, a resource will be created.
  • If a single resource matches the search, it will be updated using the contents of the request payload.
  • If multiple resources match the search, an error will be raised and no action will be performed.

5.1.5.5Update with History Rewrite

This is a HAPI FHIR / Smile CDR extension to the FHIR specification and may not be available on all servers. In order to use this operation, you need to enable the advanced setting History Rewrite on the FHIR Storage module to true, you will also need to grant your user the FHIR_UPDATE_REWRITE_HISTORY permission.

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.

5.1.6FHIR Patch

 

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 most powerful and recommended format is a FHIR Patch) document. The FHIR Patch document is a Parameters resource that can contain various operations (add, insert, delete, replace, move). It uses FHIRPath expressions, which allow for a great deal of precision in specifying what should change.
  • Two non-FHIR patch formats are also supported. While these formats are less expressive than FHIR Patch, they are both internet RFC specifications and are therefore useful to people who are already familiar with their syntax:

The following example shows a simple patch of the resource created and updated in the examples above.

5.1.6.1Request

HTTP Method: PATCH

Address: http://localhost:8000/Patient/123

Headers:

The content type depends on the patch document format being used:

  • For a FHIR Patch payload, the content type is a standard FHIR mimetype, such as application/fhir+json.
  • For a JSON patch payload the header value would be: application/json-patch+json.
  • For a XML patch payload the header value would be: 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"
         }
     } ]
   } ]
}

5.1.6.2Response

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"
        }
    ]
}

5.1.7FHIR Delete

 

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 resource is marked as deleted, and it will no longer appear in search results.
  • The version number of the resource is incremented (essentially, a new deleted version is created).
  • Previous versions of the resource are not physically deleted.
  • The resource may be un-deleted by updating it again.

The following example shows a simple delete of the resource created and updated in the examples above.

5.1.7.1Request

HTTP Method: DELETE

Address: http://localhost:8000/Patient/123

5.1.7.2Response

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"
        }
    ]
}

5.1.8Controlling the Response Payload

 

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:

  • The Resource.meta.version number and Resource.meta.lastUpdated timestamp will be updated to reflect the new version (unless the operation was a No-Op).
  • Any tags or security labels that have been automatically carried forward from previous versions of the resource will be included.
  • Any modifications to the resource performed by interceptors will be reflected.

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.

5.1.9Storage Outcome Status Codes

 

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.

CodeDisplay
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.