RE: Re: How to compromise when designing a RESTful API?
Thanks John for the great feedback. On the source account, you'd initiate a transfer with the arguments being the target account and the amount. The result on success would be that specific transfer's ID. Ah, but you're not saying how you 'initiate a transfer'. It's one of the questions I'm struggling with. It could be: ... POST /transfer/account/123 Host: myserver.com Content-Length: XXX toAccount456/toAccount amount300/amount The problem I have with the first approach is that I'm still passing an action, i.e. doing some kind of RPC. My understanding is that in a 'pure' RESTful API the only way to specify the action is via the HTTP method (get,post,put,delete). The second solution seems even worse as it mixes the action with the resource. It *seems* like you're passing an action, but IMO what you're actually doing is turning the action into a resource, which is very powerful. That resource has a unique address, a state that can be transferred, and is only manipulated with our four magic verbs. You can even make it asynchronous by posting a transaction and polling for its status later at the resource's URL. There was actually a useful post about this sort of approach on rest-discuss a couple days ago: http://tech.groups.yahoo.com/group/rest-discuss/message/6561 And what if I want to close all accounts that have been inactive for at least a year, without retrieving them one by one? Would this approach: POST /account/?inactivityPeriod=365 Host: myserver.com Content-Length: XXX actionclose/action be more appropriate than this one? POST /account/ Host: myserver.com Content-Length: XXX actioncloseInactive/action inactivityPeriod365/inactivityPeriod If you don't want to retrieve them one by one I think you can do the same thing, invert an action into a resource with something like: GET /accounts/?inactivityPeriod=365 You get back a set of account URLs: accounts account link=/account/1234 / account link=/account/1235 / account link=/account/1236 / /accounts Which maybe you can POST to an 'account closing' resource: POST /account-closer accounts account link=/account/1234 / account link=/account/1235 / account link=/account/1236 / /accounts 201 Created Location: /account-transaction/2006-09-18-A GET /account-transaction/2006-09-18-A 200 OK transaction status=Closed resources account link=/account/1234 / account link=/account/1235 / account link=/account/1236 / /resources /transaction Since that's a separate resource, you can ask questions of it, like find the most recent transactions: GET /account-transaction/recent Or whatever. I'm pretty sure this isn't gospel, but it makes sense to me, and I think demonstrates the power of the 'resource' abstraction. Chris
Re: Re: How to compromise when designing a RESTful API?
I think the approach you're advocating forces the client to know way more about the server than it should, and to make it do way more work than it has to. POST /transfer/account/123 Host: myserver.com Content-Length: XXX toAccount456/toAccount amount300/amount It *seems* like you're passing an action, but IMO what you're actually doing is turning the action into a resource, which is very powerful. That resource has a unique address, a state that can be transferred, and is only manipulated with our four magic verbs. You can even make it asynchronous by posting a transaction and polling for its status later at the resource's URL. http://tech.groups.yahoo.com/group/rest-discuss/message/6561 So, you're suggesting that a URL like /transferMoney/account/123 is indeed a resource, especially if it is used in conjunction with a transaction (as Benjamin suggested in his post): client - server: POST /transferMoney server - client: created; location: http://myserver.com/transferMoney/789 client - server: PUT /transferMoney/789 fromAccount123/fromAccount toAccount456/toAccount amount300/amount The fact that the transfer happens inside a transaction is a server concern; the client does not need to have this knowledge. Plus, it forces the client to make 2 calls instead of one. And what if I want to close all accounts that have been inactive for at least a year, without retrieving them one by one? Would this approach: If you don't want to retrieve them one by one I think you can do the same thing, invert an action into a resource with something like: GET /accounts/?inactivityPeriod=365 You get back a set of account URLs: accounts account link=/account/1234 / account link=/account/1235 / account link=/account/1236 / /accounts Which maybe you can POST to an 'account closing' resource: POST /account-closer accounts account link=/account/1234 / account link=/account/1235 / account link=/account/1236 / /accounts Here are the issues I see with this approach: - you force the server to expose an extra search API - the result set could be huge. - increased traffic between the client and the server - you still have a RPC-sounding resource (the accountCloser) Or whatever. I'm pretty sure this isn't gospel, but it makes sense to me, and I think demonstrates the power of the 'resource' abstraction. H, it seems to me that you're bending to API to make it fit the REST model, to the point where you end up breaking the abstraction layers (client knows/does too much). I'm a REST newbie, so I might very well change my mind and adopt your point of view, but -at this point- I'm still unconvinced by this 'action as a resource' approach. -Vincent.