25

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
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.

  • 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 0: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 0:06
  • If you don't have all of the verbs, it's not completely RESTful anyway, is it? – Robert Harvey Nov 16 '14 at 0: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 0:11
  • 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 0:20
14

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.

  • +1 for the OOP example :) – 53777A Nov 16 '14 at 7:09
6

(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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.