tags:

views:

82

answers:

4

I'm rather new to REST so forgive me if this is a stupid question.

So, I have a customer resource. A customer has many credits. So, I imagine a URL for getting customer credits would be

customer/21/credits

(where 21 is a customer ID)

Now, how do I add to the credits, if I don't have the full amount of credits? E.g. a customer has 10 credits and I want to add 5. As I understand, if I'm using post I would do something like:

customer/21/credits?amount=15 (is this even correct?)

However, what if I just want to add to the existing credits? That is I want to send 5 credits and say add them to whatever the customer currently has? Do I define a kind of phantom resource such as addedCredits?

customer/21/addedCredits?amount=5

then behind the scenes, I just do credits += 5?

A: 

For starters using the URI query parameters should be a "bad smell" to you. Second you need to look at defining some mime types that your clients and server can talk in so for instance with your credit example:

If I do a GET on customer/21/credits I might get a document like this:

Content-type : application/vnd.creditstore+xml

<credits>
   <user>21</user>
   <credits>10</credits>
   <a href="/customer/21/credits/add" rel="add">Add credits to this account</a>
</credits>

This tells a client who understands your vocab that if they want to add credits to this user they need to post something to that link. This is HATEOAS (god I hate that acronym, I probably even spelled it wrong).

Now this is all completely off the top of my head, and I probably butchered that XML example but it should get you thinking in the right direction.

Gandalf
But I thought resful urls shouldn't have verbs in them. Add is a verb. I thought you should define a new resource to update - hence my idea for addedCredits.
bingle
Actually not a bad point, again I did that in like 2 seconds. The only issue I see is POST is meant to create a new resource, and adding 5 credits to an existing account doesn't seem like a POST action to me. Definitely using a URI query parameter to tell it how much to add is wrong. Perhaps issuing a PUT to the ../credits URI might be an acceptable solution. Using a custom XML doc like the one above to tell the server how many credits to add.
Gandalf
POST isn't meant to create a new resource. It's supposed to append to an existing resource.
ctford
Adding 5 credits would be a POST operation **if** a "credit" is a noun/resource/item within the system. If it's not, then you probably can't POST anything since you don't have an entity to POST.
Rob Hruska
From the question we know that *credits* is a resource, so we can POST to that.
ctford
@ctford - We know that from the URI he provided, but that's only because that's what he happened to name the URI that way. Whether or not he **intends** for "credits" to be a resource is what I'm trying to ascertain.
Rob Hruska
@ctford then how does a client create a new resource? I think you are mistaken.
Gandalf
@ctford for example go read the Atom Publishing spec and check how you create a new Atom Entry in a Collection (hint it's not PUT)
Gandalf
A: 

Ultimately the implementation is up to you. WHile URI query parameters are generally frowned upon, it doesn't mean you can't use them. Personally I would make the post URI something like:

customer/21/credits/add/5

but nothing says you can't do something like what you have or :

customer/21/credits/add?value=5

jconlin
As I understand, this isn't resful since your URLs have verbs in them
bingle
Neither of those URLs identify resources. As NG says, they incorporate verbs. In REST, the verb should be captured in the HTTP method.
ctford
+2  A: 

You need to define how you're going to treat "credits" in your system; it matters whether or not you intend to define them as resources or as an attribute of your customer resource.

In the examples below, I'm going to use XML to represent the resources/entities. This may work for you, but you'll need to have some consistent way of representing your resources in requests and responses - this will help you avoid using query parameters (e.g. http://example.com?foo=bar) to define data that belongs in the request body.

A couple of ways of representing credits:

  1. If a "credit" is an attribute of your "customer":

    <customer id="21">
      <balance>10</balance><!-- aka credit -->
    </customer>
    

    Then you might as well just GET the customer, update the credit/balance with your client, and then PUT the <customer> back to /customer/21.

  2. If a "credit" is its own resource:

    You can POST the following to /credit:

    <credit>
      <dateApplied>2009-10-15 15:00:00</dateApplied>
      <customer href="/customer/21"/>
      <amount>5</amount>
    </credit>
    

    Or you can POST the following to /customer/21/credits (assuming that URI is a list of all of the <credit>s applied to the customer):

    <credit>
      <dateApplied>2009-10-15 15:00:00</dateApplied>
      <amount>5</amount>
    </credit>
    

    This would "append" a new <credit> to the existing list. And also eliminates the need to provide the <customer> in the entity, since it's already present in the URI.

Rob Hruska
Ah, yes, it could be that I don't correctly understand what is and is not a resource.Assuming the first option you present, I think I would run into concurrency issues. If client 1 gets, client 2 gets, client 1 updates, client 2 updates. You're going to have a problem.
bingle
@NG - Yes, in a multithreaded environment you'll need to manage this. As an example, you could use some sort of locking mechanism, where clients capture and release locks on a given customer. You could also use the second option, which would help you avoid it since each addition of a credit can be summed on the server, instead of making the clients sum the balance.
Rob Hruska
I think it makes sense to post to either a *credit* or *credits* resource. If you PUT the updated customer back, then what happens if more than one client reads and PUTs at the same time? One of their updates will be lost.
ctford
Locking would probably violate the statelessness constraint, depending on how you intended to do it.
ctford
I like the way you explain this. The only part I am a little unsure of is your suggestion to POST a <credit> resource to a /credit endpoint, this seems a bit unintuitive.
Darrel Miller
@ctford - I agree with both of your comments above. Option #2, POSTing to `/credit/` or `/customer/21/credits` would alleviate both of those issues.
Rob Hruska
@Darrel - Yeah, although it's still a RESTful way to do it. However, I favor the second approach for several reasons (yours and ctford's included). By using the second approach, it also simplifies the request since the client doesn't need to include the `<customer>` reference.
Rob Hruska
I think I'm having trouble thinking restfully. In an OO sense I'm thinking in terms of customer.getCredits() where credits is just an int. So making credits its own resource seems overkill (I'm thinking it's akin to making it an aggregate root in DDD terms.) That is, a credit total doesn't exist on its own - it is always a credit total of a customer.
bingle
so if i have customer/21/credits, is credits required to be a list? or could it be an int?Also, in the second option of #2 (Or you can POST the following to /customer/21/credits"), how would you know this is appending? I thought a post would replace the resource thats there.
bingle
@NG - It would make more sense if `/customer/21/credits` were a list. If a customer issues a GET on that URI, what would you return them? And if it's just an int (i.e. the customer's current balance), how is that different than treating it as an attribute of a `<customer>` (like in option 1)? For your second question, it's up to your server implementation. If it receives a POST request to that URI, it should be the one handling the entity provided and "appending" it in the most appropriate way. A PUT will replace the resource that's there, if one exists.
Rob Hruska
This is a good discussion. I'm by no means an authority on REST, I'm just coming from my experiences. The questions being asked are definitely helping me ask more and understand more about REST principles. I implore anyone else out there who has a better understanding to point out any flaws in my logic.
Rob Hruska
OK - so lets rename credits to balance for clarity's sake. Let's also say for whatever reason I don't want to expose a customers balance to the outside world, so when you do a get on customer, you don't get the balance. However, I do want to be able to add to a customer's balance. Can I just post to customer/21 with addedToBalance=5 or something like that?
bingle
@NG - That approach seems a bit hacky, since it seems you're trying to imitate a verb with the `addedToBalance`. Other than the second part of #2 in my answer, I can't think of a better way to do this both thread-safely and RESTfully. There's probably a different, maybe better approach out there. Perhaps someone will answer with it. Additionally, IMHO, I think the second part of #2 above is nice since it also provides an easy way to audit/log/track credits, and add more information to them as well (e.g. reason, tracking id, etc.). I'm out for the day, I'll check back later this evening.
Rob Hruska
I agree with the hackiness. So, given part 2 of #2, if I posted to customer/21/credits<credits><credit>4</credit><credits>i should replace the list with the one I'm sending.If I post <credit>4<credit>I should append to the existing list.Is this correct? Is it actually up to the server to inspect the shape of the xml/json/whatever to see what it should be doing with it?
bingle
@NG - Everything that happens is **entirely** up to what you code the server to do. Your example in your last comment isn't quite correct. If you post `<credit>4</credit>` it should append, and that's correct. But in order to replace, you'd have to have something that uniquely identifies the credit you want to replace. For example, if you gave your `<credit>`s ids, you could PUT (for update) <credit>5</credit> to `/customer/21/credits/123456`, where 123456 is the id. But POST should never replace a resource.
Rob Hruska
+1  A: 

I would use the same URL.

POST to customer/21/credits with a POST variable called extraCredit set to 5. POST is supposed to be used for annotation of existing resources (or creating subordinate resources). There is no reason why you should need a new URL.

If an individual credit is a resource in your system that deserves its own URL, then the response URL from POSTing to customer/21/credits should include the URL of the new credit resource e.g. customer/21/credit/12.

You could define an XML representation of credits to POST to customer/21/credits, but I would not consider it worthwhile in this simple example. REST payloads do not have to be XML.

A URL like customer/21/addedCredits?amount=5 doesn't make sense to me because it doesn't really identify a resource. If someone issues a GET to customer/21/addedCredits?amount=5 what would you return to them?

The one thing you should definitely not do is change the state of the customer resource when someone GETs a URL like customer/21/addedCredits?amount=5. Since the title of your question acknowledges that you will need to use POST your probably realise this. GET is supposed to be safe, which means that issuing a GET shouldn't change a resource's state.

ctford
What do you mean "POST variable"?
Gandalf
I think your statement about "POST updating existing resources" is incorrect. POST is meant for creating, PUT is meant for creating/updating. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5 and http://en.wikipedia.org/wiki/Representational_State_Transfer#RESTful_web_services - granted, the **actual** implementation of the methods is up to whoever codes the server.
Rob Hruska
RE: my above comment - Actually, after some more research, I've seen different places describe POST and PUT differently with regard to Creating and Updating. Yay, inconsistency!
Rob Hruska
@Rob If you POST to a URL it should update/append to that resource. However a POST response includes a URL that can point to a new resource created. In this example, if you POSTed to customer/21/credits it would update (not overwrite) customer 21's credits, but it might return a URL to a new creditUpdate resource that represents the update you just made e.g. customer/21/creditUpdate/3
ctford
@ctford - I think that depends on the resource you're POSTing to. In this case (and I think we agree here), if POSTing to `/customer/21/credits`, we can assume that resource to be a list of credits, and the POST would then append a new `<credit>` to the list. However, if he wanted to update an existing credit, he'd use PUT to do that (e.g. PUT `/credit/[unique credit update id]`). But I think to say that POST is for updating only is a bit misleading, since it's essentially creating new resources (by appending). http://rest.blueoxen.net/cgi-bin/wiki.pl?MinimumMethods
Rob Hruska
@ROB PUT Should be used to create a resource when the client can determine what the new URI will be. POST should be used when the client cannot determine the new URI, or if a resource is going to be updated
jconlin
@jconlin - Yes, that's correct, although I'm not necessarily disputing that fact. But PUT is for modification as well. From the Protocol link on my first comment above: "If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server."
Rob Hruska