What proven design patterns exist for batch operations on resources within a REST style web service?
I'm trying to be strike a balance between ideals and reality in terms of performance and stability. We've got an API right now where all operations either retrieve from a list resource (ie: GET /user) or on a single instance (PUT /user/1, DELETE /user/22, etc).
There are some cases where you want to update a single field of a whole set of objects. It seems very wasteful to send the entire representation for each object back and forth to update the one field.
In an RPC style API, you could have a method:
/mail.do?method=markAsRead&messageIds=1,2,3,4... etc.
What's the REST equivalent here? Or is it ok to compromise now and then. Does it ruin the design to add in a few specific operations where it really improves the performance, etc? The client in all cases right now is a Web Browser (javascript application on the client side).
Great post. I've been searching for a solution for a few days. I came up with a solution of using passing a query string with a bunch IDs separated by commas, like:
...then passing that to a
WHERE IN
clause in my SQL. It works great, but wonder what others think of this approach.Not at all -- I think the REST equivalent is (or at least one solution is) almost exactly that -- a specialized interface designed accommodate an operation required by the client.
I'm reminded of a pattern mentioned in Crane and Pascarello's book Ajax in Action (an excellent book, by the way -- highly recommended) in which they illustrate implementing a CommandQueue sort of object whose job it is to queue up requests into batches and then post them to the server periodically.
The object, if I remember correctly, essentially just held an array of "commands" -- e.g., to extend your example, each one a record containing a "markAsRead" command, a "messageId" and maybe a reference to a callback/handler function -- and then according to some schedule, or on some user action, the command object would be serialized and posted to the server, and the client would handle the consequent post-processing.
I don't happen to have the details handy, but it sounds like a command queue of this sort would be one way to handle your problem; it'd reduce the overall chattiness substantially, and it'd abstract the server-side interface in a way you might find more flexible down the road.
Update: Aha! I've found a snip from that very book online, complete with code samples (although I still suggest picking up the actual book!). Have a look here, beginning with section 5.5.3:
Here are two pertinent functions -- one responsible for adding commands to the queue (
addCommand
), and one responsible for serializing and then sending them to the server (fireRequest
):That ought to get you going. Good luck!
I would be tempted in an operation like the one in your example to write a range parser.
It's not a lot of bother to make a parser that can read "messageIds=1-3,7-9,11,12-15". It would certainly increase efficiency for blanket operations covering all messages and is more scalable.
From my point of view I think Facebook has the best implementation.
A single HTTP request is made with a batch parameter and one for a token.
In batch a json is sent. which contains a collection of "requests". Each request has a method property (get / post / put / delete / etc ...), and a relative_url property (uri of the endpoint), additionally the post and put methods allow a "body" property where the fields to be updated are sent .
more info at: Facebook batch API
While I think @Alex is along the right path, conceptually I think it should be the reverse of what is suggested.
The URL is in effect "the resources we are targeting" hence:
means get the record from mail with id 1 and
means patch the mail record with id 1. The querystring is a "filter", filtering the data returned from the URL.
So here we are requesting all the mail already marked as read. So to [PATCH] to this path would be saying "patch the records already marked as true"... which isn't what we are trying to achieve.
So a batch method, following this thinking should be:
of course I'm not saying this is true REST (which doesnt permit batch record manipulation), rather it follows the logic already existing and in use by REST.
The google drive API has a really interesting system to solve this problem (see here).
What they do is basically grouping different requests in one
Content-Type: multipart/mixed
request, with each individual complete request separated by some defined delimiter. Headers and query parameter of the batch request are inherited to the individual requests (i.e.Authorization: Bearer some_token
) unless they are overridden in the individual request.Example: (taken from their docs)
Request:
Response: