stusmith
stusmith

Reputation: 14103

How do I use ETag / If-Match with multiple items

The recommended way to handle optimistic locking in a RESTful interface seems to be by returning an ETag from the GET, and supplying an If-Match on the PUT, ie:

GET /items/1  --> gives client an ETag for a single item
PUT /items/1  <-- client gives it back as If-Match for checking

How do I use this scheme with multiple items, for example if I want to batch GETting multiple items from a single URI:

GET /items    --> How do I return multiple ETags for multiple items here? 

Alternatively, if ETags/If-Match don't cope with this situation, what's a recommended approach? Or should I just roll my own?

Upvotes: 23

Views: 9643

Answers (2)

inf3rno
inf3rno

Reputation: 26139

In the case of aggregates the version of the aggregate must be the version of the last updated part. With event sourcing this is plain simple, because you can use the event id for versioning.

As of your item collection, the etag of the collection resource must be the etag of the last updated item resource. For each item you must describe the etag in their hyperlinks or if all the hyperlinks have the same version in the same resource, then you can describe the version in their containing resource.

So for example if item 4 was updated latest, then GET /items should return something like:

{
    "type": "/doc/ItemSet",
    "version": "3q2teg3234",
    "label": "The Collection of Expensive Items",
    "size": 2,
    "items": [
        {
            "type": "/doc/Item",
            "version": "f233425wfsw",
            "id": "1",
            "label": "Bugatti Veyron",
            "links": [
                {
                    "label": "Read item",
                    "type": "/doc/Item/methods/read"
                },
                {
                    "label": "Update item",
                    "type": "/doc/Item/methods/update"
                }
            ]
        },
        {
            "type": "/doc/Item",
            "version": "3q2teg3234",
            "label": "Porsche 911",
            "id": "4",
            "links": [
                {
                    "label": "Read item",
                    "type": "/doc/Item/methods/read"
                },
                {
                    "label": "Update item",
                    "type": "/doc/Item/methods/update"
                }
            ]
        },
    ],
    "links": [
        {
            "label": "List items",
            "type": "/doc/ItemSet/methods/list"
        }
    ]
}

Ofc. you can send the hyperlink descriptions like /doc/Item/methods/update in the same document too, but it is better to have something reusable, so for example:

{
    "type": "/doc/HyperLinkTemplate",
    "method": "PUT",
    "uri": "/api/items/{id}",
    "headers": {
        "ETag": "{version}"
    },
    "body": {
        "label": "{label}"
    },
    "parameters": {
        "version": {
            "type": "/doc/ResourceVersion",
            "value": {
                "type": "doc/Query",
                "query": "{context/containingResource/version}"
            },
            "writeable": false
        },
        "id": {
            "type": "/doc/Item/id",
            "value": {
                "type": "doc/Query",
                "query": "{context/containingResource/id}"
            },
            "writeable": false
        },
        "label": {
            "type": "/doc/Item/label",
            "value": {
                "type": "doc/Query",
                "query": "{context/containingResource/label}"
            },
            "writeable": true
        }
    }
}

It is up to your client how it uses the hyperlink. It is even possible to generate a HTML form from it, though REST is mostly for M2M communication.

Upvotes: 0

Sixto Saez
Sixto Saez

Reputation: 12680

tl;dr: it's OK to assign etags to a resource that contains a "collection" but it is up to the app to manage the identification/change of the items in that collection when determining a valid etag.

A resource in the REST architectural style does not imply it is a single item or a collection. It simply represents the state of the application at a given point in a workflow. A resource will contain descriptive data and hopefully, links to possible next steps at that point in the workflow. The workflow could be as simple as get an order status or complex like ordering items on an eCommerce site complete with the ability to cancel the order.

An etag is a mechanism for determining whether a particular state (resource) is up-to-date or not. Assigning an etag to a resource that returns a collection of data means that your application is identifying that state of the collection items at that point and if anything in those items changes, the prior etag value would no longer be valid. It's up to your application to decide when collection items change and how to identify the individual items. In other words, etags apply to resources but not individual components of a given resource.

Upvotes: 27

Related Questions