Transactions in DSTU-2

One of the many changes that occurred in DSTU-2 (at least in the candidate spec) was the way in which transactions are managed in REST. Not so much what a transaction is (that’s reasonably well defined) – but how it is implemented. For a start, a bundle (used for a number of purposes as well as a transaction) is now a resource in it’s own right (rather than an Atom feed).

But apart from this structural change, the transaction has become more closely aligned with the simpler REST interactions – it’s easier to see how a transaction can be thought of as a sequence of simple interactions that succeed or fail as a whole. Let’s start by taking a look at the Bundle from the perspective of Transactions, including some more tricky aspects to how IDs are managed when you want to create a resource that needs to be referenced by another within the same transaction.

The Bundle resource

A Bundle has a specific element (Bundle.type) that indicates what the bundle is being used for. The values that are applicable in this discsussion are ‘transaction’ and ‘transaction-response’- representing the transaction request and the response after the transaction has been processed by the server. Here’s a full list of the options.

Bundle.base gives the base Url for all resources in the bundle. In most scenarios this is the address of the server processing the transaction – though it’s certainly possible for resources to reference different servers. (this adds significantly to the complexity however).

Bundle.link has links – we’ll talk about them some other time.

The Bundle.entry element is where the actual resources are placed – along with quite a lot of other stuff that tidies up the overall transaction processing. There are a number of child elements to Bundle.entry, but there are 3 that we’ll focus on here:

  • resource – which is where the resource itself is placed
  • transaction – which contains information about how the server is to process this resource and appears in bundles where the type element is ‘transaction’ – ie the submitted bundle.
  • transactionResponse – information about the resource was processed. This only appears in the response bundle returned by the server .

The resource element contains the resource itself. Any FHIR resource. Not much more to say about this at the moment (though we’ll have a bit to think about when we discuss the resource ID).

The transaction element is rather more interesting. It is used in the request bundle (bundle.type=’transaction’) and has the following child elements:

  • method – the HTTP verb that describes what the client wants the server to do with this resource. The most common ones would be POST (to create a new one), PUT (to update an existing one) and DELETE (to remove one). You could also use GET if you wanted the response bundle to contain resources that weren’t in the request bundle. Note that the spec specifies the order in which the server should process resources based on the method.
  • url. This is the base url that you would use if this was a single REST type interaction. For example, if you were adding a new Observation, then you would put ‘/Observation’ here.
  • ifNoneMatch. The spec says ‘for managing cache concurrency’ – which doesn’t really help a lot. (I suspect it helps a cache server to know if it already has a copy of the resource, or needs to retieve a copy from the origin server)
  • ifMatch. This is used when performing an update operation (method = PUT) to ensure that no-one else has changed the resource since you last read it (sometimes called ‘optimistic locking’). You supply an ‘eTag’ as the value – and the update will only proceed if the eTag on the current version of the resource on the server matches the eTag you supply here. (There’s a section later on that talks about eTags)
  • ifModifiedSince. This seems to indicate that an update will only occur if the last updated date of the resource on the server is the same as that given in the resource – ie it’s an alternative to the eTag. Assuming that this is correct, the name seems a bit unclear perhaps it should be ‘ifDateMatches’? But I may have the meaning wrong…
  • ifNoneExist. This is an incredibly useful element that is used for conditional operations – creates and updates – depending on the value of the method. You specify a search path as the value, and:
    • if method = POST, then the the server will create the resource only if there are no resources that match that search criteria.
    • If method =PUT, then the server will update the resource if it exists, and create it if it does not.

The transactionResponse element is used in a response to a transaction (Bundle.type = ‘transaction-response’). It mirrors what the response would be if the interaction for that resource was being performed across the simple REST interface, and you can see the details on the REST API page

  • The HTTP status code – for example 201 for created, 200 for a successful update and so forth.
  • A full url to the resource that was created or updated.
  • The servers eTag value.
  • lastModified date.

Resource IDs and referencing newly created resources

From the perspective of a resource ID, most of the time, a transaction is reasonably straightforward.

  • If you are creating a new resource, then method = ‘POST’ and you don’t specify an ID inside the resource.
  • If you are updating an existing resource, then method = ‘PUT’ and you do supply an ID. (and, if the server supports it, then it will create the resource using that ID if it does not already exist).
  • Where a resource has a reference to another resource, then the reference will have the url to that resource (relative or absolute)

But what do you do when one resource refers to another resource that is being created as a result of that transaction? For example, when recording a Blood Pressure, it is common to have 3 resources: A ‘parent’ resource that states the observation is a Blood Pressure, and separate Systolic & Diastolic Observations to which it refers (using the related element).

The spec does state that the server must update any IDs it reassigns:

“If the server assigns a new id to any resource in the bundle as part of the processing rules above, it SHALL also update any references to that resource in the same bundle as they are processed.”

But doesn’t go into a lot of details about how & when that re-assignment occurs. And, according to the spec, a POST “SHALL be a FHIR Resource without an id element”. This probably needs to be clarified, but given that the test servers currently ignore any ID in a POSTed resource (rather than rejecting the operation), the following pattern seems reasonable:

If the resource being created is the target of a reference (the ‘Systolic’ & ‘Diastolic’ observations in the example above), then give it an ID (likely a UUID), and set the method to POST. Resources referencing it can then use that ID as the reference. The server will then assign a real ID and update the references accordingly, returning the updated resources in the response bundle (usually – see ‘prefer’ below).

One thing that a transaction no longer does, is to specifically return the original ID after it has been replaced – which was useful in DSTU-1 when the ‘cid:’ prefix was used to indicate a new resource, but is less so now. However, if the client does need to know what ID was assigned to a resource it has created, there are a couple of options to do this if you want to. Most of the time the client won’t care what the new ID is – so long as it has been saved this it is OK. If it does care, then there are a couple of options:

  • One solution is for it to include a ‘business identifier’ in the resource that it can use to find the updated resource in the response bundle.
  • Another is to find the entry in the transaction response. According to the spec:

In order to allow the client to know the outcomes of processing the entry, and the identities assigned to the resources by the server, the server SHALL return a Bundle with type set to transaction-response that contains one entry for each entry in the transaction, in the same order, with the outcome of processing the entry. (Incidentally, I’m not sure how GETting a new resource fits here – presumably they are added at the end of the collection)

eTag

We’ve referred to eTags (Entity Tag) a couple of time. The eTag is like a ‘hash’ for the resource, and is used when performing updates to make sure that the no-one else has updated the resource since it was read. This is often called ‘optimistic’ locking and has the basic process:

  1. Read the resource
  2. Update it on the client
  3. Save it back to the server.

Obviously it will fall down in step 3 if some other client were to update the resource (their update would be replaced) so we can modify the process like this:

  1. Read the resource, with the server returning an eTag (eg in the HTTP header)
  2. Update it on the client
  3. Save it back to the server, including the eTag (in the header if simple REST or the ifMatch element if a transaction). The server compares the submitted eTag with the eTag of the current resource – rejecting the update if they don’t match.

So it’s up to the server to decide if eTags are required (which makes sense). Incidentally, the specification for eTags doesn’t define the algorithm for eTags – so a server can decide how ‘strong’ the creation of the eTag is. The client, of course, doesn’t need to know – from its perspective it’s just an opaque token being passed back and forth.

Conformance

We’ve referred to server behavior a few times (If the server supports…). The conformance resource is the place where this behavior is specified of course. Interestingly, there currently doesn’t seem to be a way for a server to require the use of eTags.

To return the resource (or not)

And wrapping up this post, one other thing of interest is that the client can specify whether it wishes to have the actual resources returned in the response bundle. The transactionResponse element will always be present, but if the client doesn’t want the full resource to be returned in the response then they can include the ‘Prefer’ http header in the request with a value of ‘return=minimal’ – and it’s up to the server to determine the behaviour if not specified.

 

About David Hay
I'm an independent contractor working with a number of Organizations in the health IT space. I'm an 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 FHIR standard. I'm the author of a FHIR training and design tool - clinFHIR - which is sponsored by InterSystems Ltd.

7 Responses to Transactions in DSTU-2

  1. David Fallas says:

    “The resource element contains the resource itself. Any FHIR resource.”

    Now that Bundle is itself a resource – can a Bundle contain entries that contain Bundle(s)?

    And with reference to references in the paragraph following “..the following pattern seems reasonable:” it states that:

    “Resources referencing it can then use that ID as the reference”

    Shouldn’t that read “Resources [in the same Bundle] referencing it …”?

    • David Hay says:

      1. “Can a bundle contain bundles”. Well, technically it could I guess, but I’m not sure that it would be a recommended approach – due to the complexity that would arise. Was there a specific scenario you had in mind where that would be useful?

      2. Agreed – the ‘referrer’ would need to be in the bundle, as the ID is scoped to that bundle.

      cheers…

      • Lloyd McKenzie says:

        Bundles can contain bundles. (In DSTU 1, feeds could contain feeds too, just no-one implemented it because it broke things too badly.) Example is the response to a query that contains a list of FHIR Documents.

  2. David Hay says:

    Thanks Lloyd!

  3. Pingback: Creating reusable scenarios using clinFHIR | Hay on FHIR

  4. Paul says:

    This arcticle is very helpful. We are currently trying to process a transaction bundle with some conditional updates across some related resources in the bundle. The key resource we are trying to save is the MedicationOrder. However we only want to create a new prescriber (practitioner) if they dont already exist. From this article I see we would set the method to POST for the practitioner bundle.entry and set ifNoneExist value=”Practioner?identifier=123456767 (correct?)
    However the client submitting the bundle does not know the logical id for the practioner as it may or may not already exist. But I still need to reference the prescriber from the MedicationOrder. Can the reference use a business Identifer (as shown in the example below) or must it be a Logical Id for a reference value?

    ..

    • David Hay says:

      so you would create a Practitioner resource in your bundle with the identifier by which the server can determine if it already exists, ‘ifNoneExist to Identifier={the identifier} and a UUID as the id. Then the MedicationOrder would refer to the Practitioner using that id (the UUID) (not the business identifier).

      Assuming that the server supports conditional create, and that there is either no Practitioner with that identifier or one, then all should be well. If there is more than one, then that is an error.

      btw – I find it really helpful to try this stuff out using one of the reference servers – Grahames is generally the closest to the spec, but the other main ones aren’t far behind

      for more info check out this link: http://hl7.org/fhir/http.html#ccreate and the bundle reference: http://hl7.org/fhir/bundle.html

      and note that this is a topic under active discussion at the moment: check out this connectathin track on in a couple of days! http://wiki.hl7.org/index.php?title=201605_Conditional_Reference_Connectathon_Proposal

      It may be different in STU3 🙂

      cheers…

Leave a Reply

%d bloggers like this: