0
votes

I am creating a web client which has the purpose of modifying a set of database tables by adding records to them and removing records from them. It must do so atomically, so both deletion and insertion must be done with a single HTTP request. Clearly, this is a write operation of some sort, but I struggle to identify which method is appropriate.

POST seemed right at first, except that RFC 2616 specifies that a POST request must describe "a new subordinate" of the named resource. That isn't quite what I'm doing here.

PUT can be used to make changes to existing things, so that seemed about right, except that RFC 2616 also specifies that "the URI in a PUT request identifies the entity enclosed with the request [...] and the server MUST NOT attempt to apply the request to some other resource," which rules that method out because my URI does not directly specify the database tables.

PATCH seemed closer - now I am not cheating by only partly overwriting a resource - but RFC 5789 makes it clear that this method, like PUT, must actually modify the resource specified by the URI, not some subordinate resource.

So what method should I be using?

Or, more broadly for the benefit of other users:

For a request to X, you use

  • POST to create a new subordinate of X,
  • PUT to create a new X,
  • PATCH to modify X.

But what method should you use if you want to modify a subordinate of X?

4
First things first. If you want to use proper HTTP methods (as is a Restfull application) you should not have a single request touching more than one resource (if you see your tables as resources). If all this updates represent 1 single change of 1 single resource, then you should use PUTPlínio Pantaleão
I was considering the subordinate resource to be the aggregate entity described by the multiple tables - it is not a single thing I can have a pointer to, but it is a conceptual abstraction laid on top of the database structure (manipulated through procs written to support this abstraction). Could you justify why PUT is correct even though the URI does not name the resource(s) being changed?Malnormalulo
To clarify a point, the actual resource being modified - the particular database abstraction - is specified by a parameter in the request body. The URI, therefore, is definitely not a unique identifier for it, and instead identifies the request handler which does a little preprocessing.Malnormalulo
@PlínioPantaleão there is nothing wrong with a single REST request affecting many resources, as long as they do so through a single logical resource. Remember, the representations that the clients deal with have nothing to do with how the server actually stores data.thecoshman
@thecoshman That's the reason I put in parentesis my assumption the each table represents a resource. I know that there is no need for that to be true, but it is what I have understand of the problemPlínio Pantaleão

4 Answers

0
votes

To start.. not everything has to be REST. If REST is your hammer, everything may look like a nail.

If you really want to conform to REST ideals, PATCH is kind of out of the question. You're only really supposed to transfer state.

So the common 'solution' to this problem is to work outside the resources that you already have, but invent a new resource that represents the 'transaction' you wish to perform. This transaction can contain information about the operations you're doing in sequence, potentially atomically.

This allows you to PUT (or maybe POST) the transaction, and if needed, also GET the current state of the transaction to find out if it was successful.

In most designs this is not really appropriate though, and you should just fall back on POST and define a simple rpc-style action you perform on the parent.

0
votes

RFC 2616 is obsolete. Please read RFC 723* instead, in particular http://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#POST.

0
votes

First, allow me to correct your understanding of these methods.

POST is all about creating a brand new resource. You send some data to the server, and expect a response back saying where this new resource is created. The expectation would be that if you POST to /things/ the new resource will be stored at /things/theNewThing/. With POST you leave it to the server to decide the name of the resource that was created. Sending multiple identical POST requests results in multiple resources, each their own 'thing' with their own URI (unless the server has some additional logic to detect the duplicates).

PUT is mostly about creating a resource. The first major difference between PUT and POST is that PUT leaves the client in control of the URI. Generally, you don't really want this, but that's getting of the point. The other thing that PUT does, is not modify, if you read the specification carefully, it states that you replace what ever resource is at a URI with a brand new version. This has the appearance of making a modification, but is actually just a brand new resource at the same URI.

PATCH is for, as the name suggest, PATCHing a resource. You send a data to the server describing how to modify a particular resource. Consider a huge resource, PATCH allows you to send just the tiny bit of data that you wish to change, whilst PUT would require you send the entire new version.

Next, consider the resources. You have a set of tables each with many rows, that equates to a set of collections with many resources. Now, your problem is that you want to be able to atomically add resources and remove them at the same time. So you can't just POST then DELETE, as that's clearly not atomic. PATCHing the table how ever can be...

{ "add": [
  { /* a resource */ },
  { /* a resource */ } ],
  "remove" : [ "id one", "id two" ] }

In that one body, we have sent the data to the server to both create two resources and delete two resources in the server. Now, there is a draw back to this, and that is that it's hard to let clients know what is going on. There's no 'proper' way of the client of the two new resources, 204 created is sort of there, but is meant have a header for the URI of the one new resource... but we added two. Sadly, this a problem you are going to face no matter what, HTTP simple isn't designed to handle multiple resources at once.


Transaction Resources

So this is a common solution people propose, and I think it stinks. The basic idea is that you first POST/PUT a blob of data on the server the encodes the transaction you wish to make. You then use another method to 'activate' this transaction.

Well hang on... that's two requests... it sends the same data that you would via PATCH and then you have fudge HTTP even more in order to somehow 'activate' this transaction. And what's more, we have this 'transaction' resource now floating around! What do we even do with that?

0
votes

I know this question has been asked already some time ago, but I thought I should provide some commentary to this myself. This is actually not a real "answer" but a response to thecoshman's answer. Unfortunately, I am unable to comment on his answer which would be the right thing to do, but I don't have enough "reputation" which is a strange (and unnecessary) concept, IMHO.

So, now on to my comment for @thecoshman:

You seem to question the concept of "transactional resources" but in your answer it looks to me that you might have misunderstood the concept of them. In your answer, you describe that you first do a POST with the resource and the associated transaction and then POST another resource to "activate" this transaction. But I believe the concept of transactional resources are somehow different.

Let me give you a simple example:

In a system you have a "customer" resource and his address with customer as the primary (or named) resource and the address being the subordinate address. For this example, let us assume we have a customer with a customerId of 1234. The URI to reach this customer would be /api/customer/1234. So, how would you now just update the customer's address without having to update the entire customer resource? You could define a "transaction resource" called "updateCustomerAddress". With that you would then POST the updated customer address data (JSON or even XML) to the following URI: POST /api/customer/1234/updateCustomerAddress. The service would then create this new transactional resource to be applied to the customer with customerId=1234. Once the transaction resource has been created, the call would return with 201, although the actual change may not have been applied to the customer resource. So a subsequent GET /api/customer/1234 may return the old address, or already the new and updated address. This supports well an asynchronous model for updating subordinate resources, or even named resources.

And what would we do with the created transactional resource? It would be completely opaque to the client and discarded as soon as the transaction has been completed. So the call may actually not return a URI of the transactional resource since it may have disappeared already by the time a client would try to access it.

As you can see, transactional resources should not require two HTTP calls to a service and can be done in just one.