Updating a resource using patch

We’ve received a bit of pushback from the community regarding our proposal to use custom operations for updating the Patient in NHI upgrade project. But the development team really isn’t keen on using PUT updates of the complete resource – the main reason being that of ‘accidental’ data changes when the updater doesn’t return the parts of the resource that should remain unaltered. (Apparently, this is not uncommon with messaging based updates that are kind of similar).

I mentioned in that post that we didn’t really take a close look at PATCH updates – but a couple of days ago I saw this trail in the FHIR chat which is about a similar project (slightly different requirements) and thought I really should take a closer look at PATCH.

PATCH works by creating a ‘patch document’ that specifies the changes that the client wishes to make to a resource, and submits that to the server. The server then applies those changes to the indicated resource (or course, it can reject a change if it not appropriate – for example the client doesn’t have permission to make the change) and optionally returns the updated resource.

Each change in the patch document (and there can be multiple) specifies:

  • the operation that is to be performed (eg add or remove)
  • the location in the resource to apply the operation
  • the new value (if an add or change)

There are a number of different formats that can be used for the patch document – I’m going to use the JSON version here, (which is an existing internet standard) as it’s simple and avoids namespace issues that you get with XML. FHIRPath is another option.

Let’s see how that can work (the details are in the FHIR spec of course).

I’ll start with a simple (actually empty) patient that I’ll PUT to the server (I’m going to need the resource id for the patch operations, so may as will specify it up front for these examples). And, I’ll use my favourite FHIR server – HAPI – for testing.

Here’s the initial resource:

{
    "resourceType": "Patient",
    "id" : "patchTest"
}

We’ll start with a simple update – changing the gender. According to the standard, if the target element is an object (i.e. not an array) and already exists then the value is replaced – otherwise it is added. Sounds perfect!

Here’s the patch document:

[{ "op": "add", "path": "/gender", "value": "female" }]

Note that it is in an array as there can be any number of operations in a single PATCH call. I’m simply wanting to make the patient a female in this case.

I submitted it to the server as a PATCH – [server]/Patient/patchTest – using the mime type of application/json-patch+json and the resource is updated and returned – here’s what I got back:

{
  "resourceType": "Patient",
  "id": "patchTest",
  "meta": {
    "versionId": "2",
    "lastUpdated": "2019-08-11T21:05:01.812+00:00"
  }
  "gender": "female"
}

 

So that was easy. What about a more complex operation – say adding an Address? Because this element can repeat, it is represented in the resource instance as an array. It’s absolutely possible to adjust an array, but unlike an object, the array will not be automatically created if it doesn’t exist. So, the contents of the patch document will be different if there is no address already – it needs to be created first. In the NHI project this shouldn’t be an issue as the client is required to read the resource first (regardless of how we implement update) so will know if there is already an address or not – in fact we’ll probably have optimistic locking to ensure that only one client is updating a resource at a time.

So here’s the patch document:

[
    { "op": "add", "path": "/address", "value":[]},
    { "op": "add", "path": "/address/0", "value": 
        {
            "use" : "home",
            "line" : ["23 thule st","avon"],
            "city" : "Big Smoke",
            "country" : "erewhon",
            "text":"23 thule st"
        } 
    }
]

 

And voila! One address added…. (In the interests of space I won’t show the updated resource from now on – but it’s easy to try this for yourself). If you were to try this without the first line (and there was no address), then the operation will fail.

And note that I specified the first position in the array – this is needed for arrays in PATCH, though not significant for FHIR.

What about extensions? No problem – this works just fine:

[
    {"op": "add", "path": "/address", "value":[]},
    { 
    "op": "add", "path": "/address/0", "value": 
        {    
         "extension": [
             {
             "url":"http://hl7.org.nz/fhir/StructureDefinition/buildingName",
             "valueString" : "Big Green building"
             }
         ],
        "use" : "home",
        "line" : ["23 thule st","avon"],
        "city" : "Big Smoke",
        "country" : "erewhon",
        "text":"23 thule st"
        } 
    }
]

Note that in this example I still included the ‘add empty address’ element first. This had the effect of first ‘clearing’ the existing addresses and then adding in the one I wanted. This may not be the behavior we want – but we can manage this with server side logic. For example, the server could reject any patch document that attempted to clear all addresses when the target resource already had addresses – insisting that addresses be explicitly removed rather than a wholesale clearance.

To remove an element from an array – e.g. a particular address – you use the remove operation – this works on objects as well. So the following patch document removes the first address:

[
    {"op": "remove", "path": "/address/0"}
]

So for adjusting the array, you need to know the position of the element in the array to remove. This shouldn’t be a problem as the client will have just loaded the patient, and optimistic locking ensures that no-one else has changed it in the interim.

If we wanted to be really sure, then we could require that the client submit the actual address that they are removing so the server can check – this could be done using the test operation. The document below will remove the address only if it has a single text element with the value of “simple address”

[
    {"op": "test", "path": "/address/0","value": {"text":"simple address"}},
    {"op": "remove", "path": "/address/0"}
]

To replace an existing address, we could either do a remove then an add:

[
    {"op": "test", "path": "/address/0","value": {"text":"simple address"}},
    {"op": "remove", "path": "/address/0"},
    {"op": "add", "path": "/address/0","value": {"text":"complicated address"}}
]

Or – there is a replace operation defined as well:

[
    {"op": "test", "path": "/address/0","value": {"text":"simple address"}},
    {"op": "replace", "path": "/address/0","value": {"text":"complicated address"}}
]

(Using the test operation to make sure that the right address is specified)

The examples above assume that you have the resource id – if you don’t, then a conditional patch is defined where the operation is performed against the type and search parameters are defined to locate the resource to be updated (and the update is performed only if there is a single matching resource) – the same as conditional update.

So it look like PATCH could meet our requirements. Something to think about.

 

 

 

About David Hay
I'm an independent contractor working with companies like Orion Health and Rhapsody, HL7 Fellow, Chair emeritus of HL7 New Zealand and a co-chair of the FHIR Management Group. I have a keen interest in health IT, especially health interoperability with HL7 and the new FHIR standard.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: