26

While creating a RESTful API, should I use HTTP Verbs on the same URL (when it's possible) or should I create an specific URL per action?

For example:

GET     /items      # Read all items
GET     /items/:id  # Read one item
POST    /items      # Create a new item
PUT     /items/:id  # Update one item
DELETE  /items/:id  # Delete one item

Or with specific URLs like:

GET     /items            # Read all items
GET     /item/:id         # Read one item
POST    /items/new        # Create a new item
PUT     /item/edit/:id    # Update one item
DELETE  /item/delete/:id  # Delete one item
53777A
  • 1,706
  • 13
  • 18

3 Answers3

46

In your latter scheme, you keep verbs in the URLs of your resources. This should be avoided as the HTTP verbs should be used for that purpose. Embrace the underlying protocol instead of ignoring, duplicating or overriding it.

Just look at DELETE /item/delete/:id, you place the same information twice in the same request. This is superfluous and should be avoided. Personally, I'd be confused with this. Does the API actually support DELETE requests? What if I place delete in the URL and use a different HTTP verb instead? Will it match anything? If so, which one will be chosen? As a client of a properly designed API, I shouldn't have to ask such questions.

Maybe you need it to somehow support clients that cannot issue DELETE or PUT requests. If that's the case, I'd pass this information in an HTTP header. Some APIs use an X-HTTP-Method-Override header for this specific purpose (which I think is quite ugly anyway). I certainly wouldn't place the verbs in the paths though.

Go for

GET     /items      # Read all items
GET     /items/:id  # Read one item
POST    /items      # Create a new item
PUT     /items/:id  # Update one item
DELETE  /items/:id  # Delete one item

What's important about the verbs is that they are already well-defined in the HTTP specification and staying in line with these rules allows you to use caches, proxies and possibly other tools external to your application that understand the semantics of HTTP but not your application semantics. Please note that the reason you should avoid having them in your URLs is not about RESTful APIs requiring readable URLs. It's about avoiding unnecessary ambiguity.

What's more, a RESTful API can map these verbs (or any subset thereof) to any set of application semantics, as long as it doesn't go against the HTTP specification. For example, it is perfectly possible to build a RESTful API that only uses GET requests if all operations that it allows are both safe and idempotent. The above mapping is just an example that fits your use case and is compliant with the spec. It doesn't necessarily have to be like this.

Please also mind that a truly RESTful API should never require a programmer to read extensive documentation of available URLs as long as you conform to the HATEOAS (Hypertext as the Engine of Application State) principle, which is one of the core assumptions of REST. The links can be utterly incomprehensible to humans as long as the client application can understand and use them to figure out possible application state transitions.

toniedzwiedz
  • 1,345
  • 4
  • 16
  • 25
  • 4
    In the absence of `PUT` and `DELETE`, I would favor adding it to the path, not differentiating it with a query string. It's not a query string modification to an existing operation; it's a *separate operation.* – Robert Harvey Nov 16 '14 at 00:01
  • 4
    @RobertHarvey in this case, I'd call it a hack anyway. As you say, it's an _operation_ and that's not something I'd put in the path when designing an API that aims to be RESTful. Placing it in the query string just seems less invasive. It does prevent caching but I don't think responses to this kind of requests should be cached anyway. It also allows the consumer of the API to easily indicate the method without parsing or constructing the URL. Ideally, a truly RESTful API should provide the hyperlinks without requiring clients to build URLs themselves. – toniedzwiedz Nov 16 '14 at 00:06
  • If you don't have all of the verbs, it's not completely RESTful anyway, is it? – Robert Harvey Nov 16 '14 at 00:09
  • @RobertHarvey true but I treat these as a fallback, not as the intended design. I imagine the API should support actual HTTP methods and if some client cannot implement them for any reason, they can just replace their usage with these query params. A proxy could even grab these on the fly and transform the requests into ones using genuine HTTP verbs so the server doesn't even need to care. Few APIs around are truly RESTful. When it comes to generic web APIs, it's a matter of taste really. Personally, I'd go for clean URLs. Easier to understand IMHO. – toniedzwiedz Nov 16 '14 at 00:11
  • Well, query parameters are intended for... erm, query parameters. Things like ID's, filter strings and the like, not to differentiate GET and POST. I've never seen them used in that fashion in a production environment. I'm happy to be proven wrong, however. – Robert Harvey Nov 16 '14 at 00:15
  • 1
    @RobertHarvey as explained, it's hardly the intended way to use them. I just find this the lesser of two evils for when you have to overcome client limitations. I do recall reading a documentation for such an API but I'll have to do some excavation in my browser history/bookmarks to find it. Now that I think of it, a header might be better in this case. Would you agree? – toniedzwiedz Nov 16 '14 at 00:20
  • See here: http://stackoverflow.com/q/3730777 – Robert Harvey Nov 16 '14 at 01:26
  • @RobertHarvey There's never been a requirement in the RESTful architectural style that all of the HTTP verbs have to be implemented on resources.Where did you hear that?The technically correct interpretation of the HTTP Uniform Interface consistent with REST is that *if you are going to use HTTP* you should use HTTP verbs consistent with their purpose.You don't do /DELETEs with /POSTs, you don't execute unsafe side-effects with /GETs etc etc etc. It is perfectly valid to have your API not support /DELETE and still be RESTful.REST is extremely simple and elegant. Don't overcomplicate it. – K. Alan Bates Jun 30 '15 at 11:10
  • @K.AlanBates I'm not saying this is how the verbs should be interpreted. I just explained why what the OP suggested was a bad idea and gave an example consistent with the protocol. I realize it's perfectly fine to implement a REST API with different application semantics than what's suggested in this post. Perhaps I should make it clearer. – toniedzwiedz Jun 30 '15 at 12:52
  • @toniedzwiedz Sorry for the lack of clarity.I was commenting in response to your comment where you agreed with Robert Harvey that "not implementing all of the HTTP verbs is not completely RESTful."His statement and your agreement with it are not correct.It is perfectly acceptable for a RESTful API to implement the portion of HTTP's uniform interface that makes sense for the domain.It may be improper to support `/DELETE` or `/PATCH` requests, for instance.Their inclusion or absence is orthogonal to "RESTful" concerns unless you are using those verbs in a way that violates HTTP convention – K. Alan Bates Jun 30 '15 at 13:56
  • @K.AlanBates: I've seen query strings used to support different "write" operations under POST, such as a PUT or a DELETE. GET is a fundamentally different operation, and I don't believe I said anything about resources per se. – Robert Harvey Jun 30 '15 at 14:09
  • @RobertHarvey I've heard of those magnificent beasts too; in ancient horror stories from the long, long ago when Java and DCOM roamed the earth. (back to topic) ...you said "if you don't have all of the verbs, it's not completely RESTful anyway, is it?" to which my reply was focused. – K. Alan Bates Jun 30 '15 at 14:54
  • @K.AlanBates It wasn't so long ago that browsers didn't fully support all the verbs, or at least software developers didn't think they supported all the verbs, which is why we're having this pleasant conversation. – Robert Harvey Jun 30 '15 at 15:07
  • @RobertHarvey (re:Java&DCOM) I was being facetious :) ...at any rate, we've deviated from the point I was making that you are not required to implement "all" of the HTTP verbs for an API to be RESTful. You don't even have to implement HTTP. If you *do* use HTTP, interactions with the resource server should be consistent with HTTP's semantics. If you do not wish to support any particular portion of HTTP's defined verbs, your API does not inherently become less RESTful. That was the point. – K. Alan Bates Jun 30 '15 at 15:24
  • @RobertHarvey ...wait a second lol. I completely glossed over this in your comment. Why in the world would the implementation details of (very old) web browsers have any bearing on developers' understanding of HTTP1.1? XHRs have had mainstream support since IE5 released in '99. Unfortunately, that is closing in on being 20 years ago. You consider 16 years of internet technology to be "not so long ago?" I call it *aeons* ago. – K. Alan Bates Jun 30 '15 at 17:51
  • @K.AlanBates: Which is why I qualified my remarks with "or at least software developers didn't think they supported all the verbs." I take it you think nobody writes applications this way anymore? (I doubt that) – Robert Harvey Jun 30 '15 at 17:54
  • @RobertHarvey I'm well aware there are people that write applications in the way you have described where any write operation is performed under a POST (for instance); they are rightly called amateurs. That's fine, but why muddle a technical discussion -especially concerning something as touchy as REST- with regard for the misguided techniques of amateurs? – K. Alan Bates Jun 30 '15 at 18:06
  • @K.AlanBates: Clients that don't support PUT or DELETE is a premise offered in the original answer. – Robert Harvey Jun 30 '15 at 18:07
  • @RobertHarvey I mean...sure. (I cut the part from my last comment saying that there must be a *very,very,very* good reason to -as an example- tunnel RPC calls through to a server with HTTP POSTs ) There is a small set of use cases where your techniques are constrained. But those constraints are not consistent with generally good practices; they are built atop the realities of poorly designed or very old systems. When faced with those circumstances, you do what you have to do to get the job done.But those edge cases should not be used to inform general design guidance. – K. Alan Bates Jun 30 '15 at 18:18
  • Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/25376/discussion-between-k-alan-bates-and-robert-harvey). – K. Alan Bates Jun 30 '15 at 18:25
15

The first one.

A URI/URL is a resource identifier (hint in the name: uniform resource identifier). With the first convention, the resource being talked about when you do "GET /user/123" and the resource being talked about when you do "DELETE /user/123" is clearly the same resource because they have the same URL.

With the second convention, you cannot be sure that "GET /user/123" and "DELETE /user/delete/123" actually are the same resource, and it seems to imply that you're deleting a related resource rather than the resource itself, so it would be rather surprising that deleting /user/delete/123 actually deletes /user/123. If you have every operations work on different URLs, the URI is no longer working as a resource identifier.

When you say DELETE /user/123, you are saying "delete 'user record with id 123'". While if you say DELETE /user/delete/123, what you seem to be implying is to "delete 'user deletor record with id 123'", which is probably not what you want to say. And even if you use the more correct verb in this situation: "POST /user/delete/123" which says "do the operation attached to 'user deletor with id 123'", it is still a roundabout way to say delete a record (this is akin to nounification of a verb in English).

One way you can think about URL is to treat it like pointers to objects and resources as objects in object-oriented programming. When you do GET /user/123, DELETE /user/123, you can think think of them as methods in the object: [/user/123].get(), [/user/123].delete() where the [] is like a pointer dereferencing operator but for URLs (if you know a language that have pointers). One of the underlying principle of REST is uniform interface, i.e. to have a small and limited set of verbs/methods that works for everything in a vast network of resources/objects.

Therefore, the first one is better.

PS: of course, this is looking at REST in the purest manner. Sometimes practicality beats purity, and you need to make concessions for brain-dead clients or framework that makes it hard to do proper REST.

Lie Ryan
  • 12,291
  • 1
  • 30
  • 41
7

(sorry, my first time through I missed the /edit/ and /delete/ in (2)... )

The idea of the URI is that it is an identifier of an addressable resource, rather than a method invocation. So the URI should point to a specific resource. And if you deference the URI, you should always get the same resource.

That is, you should think about URIs in the same way you think about the Primary Key of a row in a database. It uniquely identifies something: Universal Resource Identifier.

So whether you use plural or singular, the URI should be an identifier rather than an invocation. What you're trying to do goes in the method, namely: GET (get), PUT (create/update), DELETE (delete) or POST (everything else).

So "/item/delete/123" breaks REST because it doesn't point to a resource, it's more of a method invocation.

(Also, just semantically, you should be able to GET a URI, decide it's outdated, and then DELETE the same URI -- because it is an identifier. If the GET URI doesn't have "/delete/" and DELETE does, then that goes against HTTP semantics. You're broadcasting 2 or more URIs per resource where 1 will do.)

Now, the cheat is this: there's no real clear definition of what is and isn't a resource, so the common dodge in REST is to define a "processing noun" and point the URI to that. That's pretty much a word game, but it satisfies the semantics.

So if, for example, you really couldn't use this for some reason:

DELETE /items/123

you could declare to the world you have a "deletor" processing resource and use

POST /items/deletor  { id: 123 }

Now, that looks a lot like RPC (Remote Procedure Call), but it falls through the vast loophole of the POST specification's "data processing" clause named in the HTTP spec.

However, doing that is kind of exceptional, and if you can use the common PUT for create / update, DELETE for delete, and POST for append, create, and everything else, then you should, because it's a more standard use of HTTP. But if you have a tricky case like "commit" or "publish" or "redact", then the case for using a processor noun satisfies the REST purists and still gives you the semantics you need.

sea-rob
  • 6,841
  • 1
  • 24
  • 47