34

Before I decided to ask this question I have searched quite a long for the answer but I haven't found any satisfactory. (e.g. Examples of the best SOAP/REST/RPC web APIs? And why do you like them? And what's wrong with them?)

And the problem is actually quite simple. I have an object/resource named Account. My REST API supports all CRUDs with GET, POST, PUT and DELETE already with proper error handling, status codes etc.

Additionally however I want to expose an API ("command") to activate and deactivate selected Account resource. Even if the "isActive" is a property of the Account I don't want to use just the Update from my CRUD of the whole Account.

I know it is easy to violate REST principles and make RPC style design with such design like this:

PUT /api/account/:accountId/activate

PUT /api/account/:accountId/deactivate

So what is the best solution for this use case?

My current idea is to use PUT and DELETE verbs like this (to treat it as a sub-resource) as proposed here http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#restful:

PUT /api/account/:accountId/isActive // for activate

DELETE /api/account/:accountId/isActive // for deactivate

What are your solutions?

Community
  • 1
  • 1
Adam Bogdan Boczek
  • 1,720
  • 6
  • 21
  • 34
  • I don't think that it's an "RPC" design. It's a "Message"-based design. Although, why not POST in the first example? – user2864740 Feb 15 '14 at 08:32
  • It seems to be standard to use POST as non-idempotent method. I'm still not sure what is the best design pattern for such "command" cases. – Adam Bogdan Boczek Mar 02 '16 at 08:09
  • 2
    I found this interesting talk about "REST-Ful API Design" https://youtu.be/oG2rotiGr90 and use rules it defines e.g. PUT/PATCH for actions like "activate", "open", "install" and so on. – Adam Bogdan Boczek Dec 12 '16 at 07:56

4 Answers4

18

How about coming up with a noun for the feature you want to modify - 'status' in this instance. This would then become a sub resource of the parent entity. So for your case I would model the URI as follows:

/api/accounts/{accountId}/status

If the 'update' semantics are idempotent then PUT would be most appropriate, else that would need to be a POST (e.g if nonces are involved and are invalidated by the service). The actual payload would include a descriptor for the new state.

Note, I pluralized 'accounts' since you can have multiple of those, but status is singular since your account can have only one state.

Michael-7
  • 1,739
  • 14
  • 17
  • Interesting approach. However in this way we will introduce something new from the functional point of view (a new property?), thus increase complexity. However, as I said gut point when designing the core domain model. – Adam Bogdan Boczek Mar 26 '14 at 19:54
  • Its old thread but PUT "should" have body – Jigar Shah Aug 10 '21 at 22:11
5

The POST method would create the resource 'account'. Active can be seen as one of the properties of the resource 'account'. Hence it should be a PUT request.

I would say even deactivate must be a PUT request as the account resource will still exist.

To activate an account you can set a property on the resource. That is:

/api/account/{accountId}?activate=true

To deactivate:

/api/account/{accountId}?activate=false

A GET request on the account would return a JSON with the activate value in it.

A DELETE request should completely delete the account resource.

Steve Chambers
  • 37,270
  • 24
  • 156
  • 208
Neeraj Krishna
  • 1,527
  • 2
  • 16
  • 22
  • I'm confused as to why this answer was down voted. I've seen this used before and it seemed appropriate. – Steve Wall Oct 31 '14 at 14:44
  • Neerja, I think there is a reluctance to use query (?) parameters in this way, but instead to limit there use to selection criteria/filters. – Herc Jan 11 '15 at 20:17
  • 9
    We should limit query parameters to filtering – Jamal Sep 18 '15 at 11:14
  • I don't think that's a good answer. Firstly, PUT should "replace all current representations of the target resource with the request payload" (https://tools.ietf.org/html/rfc7231#section-4.1) which means you should PUT ALL the payload, not just single `status` field. You'd better suggest PATCH. – egvo Apr 23 '21 at 08:51
  • Secondly. "GET, HEAD, OPTIONS, and TRACE methods are defined to be safe" (https://tools.ietf.org/html/rfc7231#section-4.2.1) which means user does not have to care much about sending them - he should be assured that his actions with GET requests will not do any harm. Request with `?activate=false` params WILL do harm. In contrast, POST, PUT, PATCH, DELETE aren't safe and while user sending them he must think of he is responsible for any harm it'll do. – egvo Apr 23 '21 at 08:56
  • Thirdly, we may think of using `?activate=false` param with POST/PUT/PATCH, but it is a bad idea too. Here's a good explanation why we should not do that: https://stackoverflow.com/a/26717908/7744106 In short, "_query_ string does not (semantically) make sense" with the _post_ command (you're not querying data, you're posting it). Besides, POST requires `body`, so give it a body, not query params. – egvo Apr 23 '21 at 09:15
  • So I think this answer is good, but now it is out of date. – egvo Apr 23 '21 at 09:21
5

PATCH is the most appropriate method in this case. Please find more at RESTful URL for "Activate"

  • 1
    The problem with this approach is that it hides the semantic in the body of the request. I wouldn't do it even if it might be correct from the puristic design point of view. – Adam Bogdan Boczek Mar 23 '18 at 16:53
1

First off, PUT is appropriate compared to POST, because you are creating a resource to an already-known location. And, I think, there's no dilemma about DELETE. So at first glance, your current approach seems to beat the alternatives.

I used to think the same way, until I implemented my own REST api, in which I wanted the admin to be able to set an account in a deactivated - yet not deleted, just "banned" - state. When I gave it a little more thought, I decided to do it vice versa.

Let me explain. I like to see the activation resource as "the option to activate the account". So if a url like /account/foo/activation exists, it could only mean that the account is not activated and the user has the right to activate it. If it doesn't exist, the account is either already activated or in a banned state.

Consequently, the only rational thing to do in order to activate the account is to try and DELETE the resource. And, in order to enable activation, an admin would have to PUT an activation resource.

Now, the question that comes to mind is how do you distinguish a banned account from an already activated one. But since a ban could be seen as a resource too, you could create a /account/foo/ban resource collection. In order to ban an account, probably for a fixed amount of time, you just POST a resource under that collection, containing all the details of the ban.

geomagas
  • 3,230
  • 1
  • 17
  • 27