I am designing a web service to regularly receive updates to lists. At this point, a list can still be modeled as a single entity (/lists/myList
) or an actual collection with many resources (/lists/myList/entries/<ID>
). The lists are large (millions of entries) and the updates are small (often less than 10 changes).
The client will get web service URLs and lists to distribute, e.g.:
- http://hostA/service/lists: list1, list2
- http://hostB/service/lists: list2, list3
- http://hostC/service/lists: list1, list3
It will then push lists and updates as configured. It is likely but undetermined if there is some database behind the web service URLs.
I have been researching and it seems a HTTP PATCH using the JSON patch format is the best approach.
Context and examples: Each list has an identifying name, a priority and millions of entries. Each entry has an ID (determined by the client) and several optional attributes. Example to create a list "requiredItems" with priority 1 and two list entries:
PUT /lists/requiredItems
Content-Type: application/json
{
"priority": 1,
"entries": {
"1": {
"color": "red",
"validUntil": "2016-06-29T08:45:00Z"
},
"2": {
"country": "US"
}
}
}
For updates, the client would first need to know what the list looks like now on the server. For this I would add a property "revision" to the list entity.
Then, I would query this attribute:
GET /lists/requiredItems?property=revision
Then the client would see what needs to change between the revision on the server and the latest revision known by the client and compose a JSON patch. Example:
PATCH /list/requiredItems
Content-Type: application/json-patch+json
[
{ "op": "test", "path": "revision", "value": 3 },
{ "op": "add", "path": "entries/3", "value": { "color": "blue" } },
{ "op": "remove", "path": "entries/1" },
{ "op": "remove", "path": "entries/2/country" },
{ "op": "add", "path": "entries/2/color", "value": "green" },
{ "op": "replace", "path": "revision", "value": 10 }
]
Questions:
- This approach has the drawback of slightly less client support due to the not-often-used HTTP verb PATCH. Is there a more compatible approach without sacrificing HTTP compatibility (idempotency et cetera)?
- Modelling the individual list entries as separate resources and using
PUT
andDELETE
(perhaps withETag
and/orIf-Match
) seems an option (PUT /lists/requiredItems/entries/3
,DELETE /lists/requiredItems/entries/1
PUT /lists/requiredItems/revision
), but how would I make sure all those operations are applied when the network drops in the middle of an update chain? Is a HTTP PATCH allowed to work on multiple resources? - Is there a better way to 'version' the lists, perhaps implicitly also improving how they are updated? Note that the client determines the revision number.
- Is it correct to query the revision number with
GET /lists/requiredItems?property=revision
? Should it be a separate resource like/lists/requiredItems/revision
? If it should be a separate resource, how would I update it atomically (i.e. the list and revision are both updated or both not updated)? - Would it work in JSON patch to first test the revision value to be
3
and then update it to10
in the same patch?