> Why not simplify by removing parameters from the URL altogether?
Caching. Put a caching proxy in front of your api endpoint and enjoy.
This is the same reason you'll want to do:
/api/customer/1234/order/abcd
instead of: /api?customer=1234&order=abcd
Because even if your cache includes query strings (which it may, and you can configure it to), you lose order. Now you also have to configure your cache to ignore ordering on the query string, and that's a lot less obvious (and probably not even HTTP compliant?).You have missed the point of REST. REST is an architectural pattern for a World Wide Web global-scale system. Your API may not fit that description. Thus, REST may not be appropriate for your API.
Following the REST patterns means you can take advantage of things like standard agnostic intermediaries for caching & filtering. You can switch or even spread across service providers by changing domain names in URLs. You can mitigate network issues through guaranteed idempotence and using conditional headers like if-none-match. These are all issues that you'll probably have to rediscover and re-solve on your own, if your API lives long enough.
It is not about SEO or visually "clean" URLs. REST says nothing about that, despite many misconceptions otherwise. In fact, in a truely RESTful service, resource URLs can be obscure garbage like http://example.com/A13D-DE45-32BC. If you're using the HATEOS principle, those URLs will be picked up from links in your responses.
"Pidgeonholing" your errors to standard HTTP error codes means that you've followed a shared standard, rather than inventing your own from scratch. Also, 4xx errors generally refer to something in the client's request, while 5xx errors generally mean something has gone wrong while generating a response on the server side.
If you don't need any of these things - because, say, you're building an API that will only be used between two parts of your own small system - then don't worry about REST. Go ahead and build something along RPC lines.
You might regret it someday, if you find yourself needing the scaling characteristics of the WWW. But, many systems never grow to that point anyway.
Pure REST is great for toy examples, where every domain entity fits neatly into a resource and every operation is a CRUD one that can be represented as an HTTP verb. So there is a million tutorials explaining how to build a REST API for a blog with BlogPost and Author resources, or an ordering system with Customer and Order resources.
However, when you move beyond these simple examples and start trying to build a REST API for a messy, complex system, you immediately run into trouble. How do you deal with non-CRUD operations? How do you deal with long running processes? Or entities in an indeterminate state? How do you aggregate multiple resources into individual request/responses for performance? How do you de-duplicate identical entities within an aggregated response? How do you return only particular fields? How do you handle paging and filtering? How do you handle transactions? How do you handle authentication and security restrictions?
All of these things can be solved, but by the time you've finished, your API will be far from easy to understand. It will need a load of accompanying documentation to make it usable, and will have a bunch of weird corners where you're initiating operations as a side effect of setting entity flags, or you've noun-ed verbs in order to turn an abstract operation into a resource. E.g. PUT /server-reboot-attempt.
What an incredibly clueless post. I thought we’d left that level of misunderstanding behind us a few years ago.
There is no such thing as a RESTful URL. Characters in URLs don’t matter.
GET and POST have different semantics. Ignoring the benefits of caching is just dumb.
The semantics of status codes and verbs are defined pretty clearly. You might have to read an actual spec.
A paragraph starting with “A core difference between REST and RESTful …” physically hurts.
I can’t believe I just wasted time actually commenting on this.
If you are mapping your RPC-style API into a REST interface, you are doing it wrong.
REST as an architectural style wants you to think as your API as a collection of resource representations that can be manipulated through simple verbs (as the one present in HTTP).
It forces you to think about idem-potency, caching, namespacing, immutability and robustness.
If your API doesn't have abstractions for long running processes or more complex business transactions, you don't have a RESTful API and need to adapt it.
Furthermore, if payload size and minimizing the number of requests is your most important goal, why are you using HTTP in the first place? It's not the right solution for you.
> In other languages API calls don’t return with function pointers to related functions.
Let me introduce you to a little something called objects.
Completely beside the point of the article, but just seeing a url like "/customer/33245/order/8769" makes me anxious that the author might give multiple customers the same order number.
Congratulations, you've discovered RPC.
It would have been nice if he proposed something innovative moving forward, but this is a step back to RPC-structured APIs. After reading this though, I had to check to make sure it wasn't April 1st.
How the url is formatted is irrelevant for REST.
The criticism they start with is weird. They take this API call:
OrderDTO Customer::GetOrder(int customerID, int orderID)
And this HTTP call: GET /customer/33245/order/8769
And ask:> If we put the function name between the parameters themselves, then it leads to the question, what part of this URL is the endpoint?
But the thing that is between the parameters is "order" not the function name "GetOrder". If you look at an alternative form in which one might (more idiomatically, in some languages) see the API call, the relation to the HTTP call is more clear:
Customers[customer_id].Orders[order_id] # => Order
> REST URLs look ‘cleaner’, but are they easier to understand?Yes.
They go on to say:
> A simpler API, similar to our original function call would have no mixing of parameters with the name, and clear definitions of the parameters being passed in.
With this example:
GET /customer/getOrder?customerID=33245&orderID=8769
How is that simpler? Sure, depending on how you've implement the backend, it may be a more direct reflection of the backend code, but there is a difference between "leaky abstraction" and "simpler". Also, why is the fact that the action is "getting" something reflected twice?> Why not simplify by removing parameters from the URL altogether? Put them in the query string, or post body as JSON.
Sure, RPC-over-POST is an alternative (older than REST) style of Web API, and if that's what floats your boat, do it. OTOH, "removing information from the URL for aesthetic reasons" may not be the best reason to move a safe operation from GET, which is defined as safe, to POST, which is neither safe nor idempotent, particularly if you are using middleware that is aware of HTTP semantics -- you've just thrown away valuable information.
> Your API can encounter many application specific exceptions while processing a request, but why try to pigeon hole your error response into a limited number of error codes designed for retrieving hypertext?
Why not choose the best error code of those available in the protocol? In a traditional programming language, would you just throw the most generic possible exception? Because that's what the "just use 200 for success and 500 for failure" approach is equivalent to.
> Verb agnostic, HTTP verbs are really only needed to identify where parameters will go – for GET it’s in the query string, For POST, in the body.
If you are going to use HTTP, why fight against it by throwing out its defined semantics? Aside from reducing the value you get from existing HTTP-semantics-aware software, you are just forcing yourself to reinvent the wheel.
> REST is a style, not a standard.
Whether REST is a style or a standard is irrelevant when your post is advocating abandoning it completely in favor of RPC-over-HTTP with deliberate disregard for HTTP's defined semantics.
Microsoft-stack developer doesn't understand how HTTP works. Film at eleven.
Wait, was this a serious post or some sort of The Onion-like satire?
REST is good, better than RPC, SOAP... finally people can understand and use APIs easily and happily. The URL /customer/33245/order/8769 is perfect, looks like a directory path and reflect the relationship between the entities. Arguing about the best url, status code and best verb is the irrelevant part in REST. Why can't people just do whatever it works for the users while respecting the standards when they can ?!
I am increasingly getting irritated with REST for our app. It takes too much thought to figure out the "correct RESTful approach" when building out an API endpoint. And then again, there are no right answers. And all the JSON serialization/de-serialization logic, for every single endpoint. Too. Much. Work.
I'm wondering why Thrift/Proto-buffers aren't popular? Even for browser-based JS clients. What're the pitfalls?
Is it just me or GET /customer/33245/order/8769 is wrong You have unique order id... then use it GET /order/8769 like that ??
How about this then?
GET /index.php?do=getOrder&customerID=33245&orderID=8769
"Does any language force you to prefix a function with 4 verbs?"
Actually, I don't really know. But for sure HTTP isn't forcing you to only use GET/POST/PUT/DELETE, even if this verbs are usually enough to design your API. It's quite funny to hear some people (I'm not talking about the author) joking about the "418 I'm a teapot" response status, which is defined in a HTTP extension, and then telling you that having only 4 methods to design an API is too restrictive.
HTTP is extensible, so why misusing it?
Well REST allows for resources to be accessed as they are: resources. Using POST/GET query parameters implies that the server should do some actual processing on these to deliver the requested resource. With REST, you can have the resource be a static file on the server, no processing required.
Thank you! I was wondering why I couldn't just use post for a function I'm writing right now. It helps to have someone else say out loud what I was thinking.
Just another kid with a keyboard and High Speed Internet.
The answer basically is caches and proxies.
Why you use GET vs POST are conventions. There are no way to enforce any rules in web services and conventions help to make the things you are looking at recognizable. And conventions do change over time. I think we need more HTTP verbs like START and STOP and I wish POST was NEW. Merge vs. update-all for PUT is still not well established and now we have PATCH. Such terrible names. I would love something like REPLACE and MERGE or something more descriptive.
I also think that your customer order example could have been done better. Something like
Your example: OrderDTO Customer::GetOrder(int customerID, int orderID) But that's the wrong side of the equation, that's the definition of the function. If you call the function it would look like this: GetOrder(myCustomerID, myOrderID)
And an equivalent REST call would look like this with a little clean up. GET /orders/?id={id}&customer-id={customer-id}
Doesn't look that much different to me. Sure you could have done GET /orders/{id}/?customer-id={customer-id}, but there is no reason why the above is wrong from a RESTful perspective. Why would you marshall a whole JSON payload to do a simple GET? You are not creating a tuple to pass the two parameters in your full-language function example, what's the reason it is required for the RESTful call?
As I mentioned above HTTP needs a couple more verbs and aliases for the existing verbs. You think of situations like:
START /timers STOP /trains
A lot of the time we jam POST and PUT in this situation and they are poor substitutes for a larger verb vocabulary. I have built a few automation projects and any actions you do can usually fit into
NEW GET UPDATE (merge) DELETE START STOP OPEN CLOSE
still limited, but pretty good at covering almost all the verbs needed. You supply the nouns.
NEW /vms START /vms OPEN /remote-desktop/1234 CLOSE /files/folder/path/to/file
Especially with persistent connections in HTTP2 and web sockets and things of the kind OPEN and CLOSE will likely be need to establish these kinds of connections. You could probably get away with using START and STOP though, such as:
START /remote-desktop/?computer=1234
As for HTTP error codes. They need an overhaul. 404 and 403 do the job fine, but giving the user an idea of whether their JSON was malformed (Syntax) vs. the data in the payload was invalid is difficult to distinguish with just 400. But the return payload should describe the error in detail. Not many conventions there to help.
As for HATEOS, I used to think it was great, but I am meh on it now. It needs refinement.
Except that RPC-structured APIs are complete garbage to consume for external developers because, surprise, they don't know or care what your internal function names are. And if you aren't thinking about the experience of real or theoretical third-party developers when building your API, why even have one at all?
If you ignore HTTP verbs and status codes you're just making it harder for other developers to grok your system. It's less about adherence to some strict standard and more about helpful signaling. A GET fetches data, POST creates new records, PUT is idempotent, DELETE deletes. 4xx status codes indicate a request error, 5xx indicate a server error. That's all useful information for someone trying to integrate with your API, but you think that instead everyone should memorize your in-house conventions. Good luck getting buy-in on that.
The only partial agreement I'd give to this is that I no longer think it's wise to nest multiple levels of resource IDs (e.g. /customer/123/order/456). This is, again, out of respect for external developers. Instead, I would have /customers/123 and /orders/456 (assuming that IDs can't be duplicated between customers). A flatter URL structure (which is, note, still REST-friendly) can be an easier-to-consume experience for other devs.