tags:

views:

35

answers:

2

This is a follow up to http://stackoverflow.com/questions/1574951/updating-a-value-restfully-with-post

How do I simply append to a resource's attribute using REST. Imagine I have customer.balance and balance is an int. Let' say I just want to tell the server to append 5 to whatever the current balance is. Can I do this restfully? If so, how?

Keep in mind that the client doesn't know the customer's existing balance, so it can't just

  1. get customer
  2. customer.balance += 5
  3. post customer

(there would also be concurrency issues with the above.)

A: 

To think about this in a REST-ful way, you would need to think about the action itself as a resource. For example, if this was banking, and you wanted to update the balance on an account, you would create a deposit resource, and then add one of those. The consequence of this would be to update the customer's balance

This also helps deal with concurrency issues, because you would be submitting a +5 action rather than requiring prior knowledge of the customer's balance. And, you would also be able to recall that resource (say deposit/51 for deposit with an ID of 51) and see other details about it (ie. Reason for deposit, date of deposit etc.).

EDIT: Realised that using an id of 5 for the deposit actually confuses the issue, so changed it to 51.

Deeksy
so i could create say:customer/21/debitscustomer/21/creditseach which would have a collection of debit and credit resources?Now, under the hood I'd just like to += the credit amount and -= the debit about to the customer.balance. This means a get to customer/21/credit/5 wouldn't be possible. Is this okay in terms of REST? Based on your answer it seems not.
bingle
To create a new debit, I would POST to customer/21/debits/new and include the 5 as amount in the post.If I wanted to get the debit with an id of 5, I would GET customer/21/debit/5
Deeksy
That should be PUT not POST for creating a new resource, of course.
Deeksy
+1  A: 

Simple, slightly ugly:

This is a simpler variation of my answer to your other question.

I think you're still within the constraints of REST if you do the following. However, I'm curious about what others think about this situation as well, so I hope to hear from others.

Your URI will be:

/customer/21/credits

You POST a credit resource (maybe <credit>5</credit>) to the URI, the server can then take the customer's balance and += it with the provided value. Additionally, you can support negative credits (e.g. <credit>-10</credit>);

Note that /customer/21/credits doesn't have to support all methods. Supporting POST only is perfectly acceptable.

However, this gets a little weird if credits aren't a true resource within your system. The HTTP spec says:

If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header.

Technically you're not creating a resource here, you're appending to the customer's balance (which is really an aggregate of all previous credits in the system). Since you're not keeping the credit around (presumably), you wouldn't really be able to return a reference to the newly "created" credit resource. You could probably return the customer's balance, or the <customer> itself, but that's a bit unintuitive to clients. This is why I think treating each credit as a new resource in the system is easier to work with (see below).

My preferred solution:

This is adapted from my answer in your other question. Here I'll try to approach it from the perspective of what the client/server are doing:

  • Client:

    1. Builds a new credit resource:

      <credit>
        <amount>5</amount>
      </credit>
      
    2. POSTs resource to /customer/21/credits

      POSTing here means, "append this new <credit> I'm providing to the list of <credit>s for this customer.

  • Server:

    1. Receives POST to /customer/21/credits
    2. Takes the amount from the request and +=s it to the customer's balance
    3. Saves the credit and its information for later retrieval
    4. Sends response to client:

      <credit href="/customer/21/credits/credit-id-4321234">
        <amount>5</amount>
        <date>2009-10-16 12:00:23</date>
        <ending-balance>45.03</ending-balance>
      </credit>
      

This gives you the following advantages:

  • Credits can be accessed at a later date by id (with GET /customer/21/credits/[id])
  • You have a complete audit trail of credit history
  • Clients can, if you support it, update or remove credits by id (with PUT or DELETE)
  • Clients can retrieve an ordered list of credits, if you support it; e.g. GET /customer/21/credits might return:

    <credits href="/customer/21/credits">
       <credit href="/customer/21/credits/credit-id-7382134">
         <amount>13</amount>
         ...
       </credit>
       <credit href="/customer/21/credits/credit-id-134u482">
         ...
       </credit>
       ...
    </credits>
    
  • Makes sense, since the customer's balance is really the end result of all credits applied to that customer.
Rob Hruska
Your approach seems the most logical one to me.
Darrel Miller
@Darrel - Thanks. I'd actually like to know if you've got an alternative. Your tags indicate you've got a pretty good interest in REST, and you probably have more experience than I. Any suggestions or improvements?
Rob Hruska
The problem is that the question has relatively trivial requirements so, with the information that we have, I don't really have anything to add. Real situations tend to get a bit more tricky, but it is amazing how many problems in REST can be addressed by POSTing to a sub-resource which is a list of "change requests".If you look at the scenario a bit more realistically, you can see that "crediting" a "customer" is a simplification. You would probably want to POST to something like /Customer/21/AccountBalance/Credits. But that is beyond the scope of the original question.
Darrel Miller
One different approach would be to model "CustomerPayments" and "CustomerPurchases" as completely separate resources and simple make the balance property of "Customer" a read-only calculated field.
Darrel Miller
I agree, I think those approaches offer more flexibility and control, and also probably allow it to grow more easily in the future. Thanks for the input.
Rob Hruska