REST was never about CRUD
tyk.io
tyk.io
By thinking this way, you can see how REST helps you re-use already-existing infrastructure---proxies, load balancers, caches, etc.---instead of spending time and effort making custom solutions. It also helps provide guidance as to when REST may not be appropriate: can you answer those "why not HTTP?" questions?
I've also found focusing on the resource/noun aspect (beyond considering caching) to be a bit of a "nerd trap"; it's possible to spend a lot of time chasing the perfect, "correct", solution. As the article notes, there's nothing wrong with "verby" URLs as long as you are using HTTP instead of doing your own thing.
Once you've made those decisions from a business perspective, you can have your services send the HTTP headers to effect that caching behavior.
You also probably should not let the browser cache it either (you’ll typically get dinged for it in pentests because your app may be used on a shared computer).
Or, if you have a CDN, you could cache closer to the end-user (assuming you have the necessary controls on your CDN to meet regulatory [and common-sense] requirements).
But end-to-end cacheability at the HTTP layer, part of the original rationale behind the REST API design style, does not make sense for most APIs anymore in 2018.
Why not? What were the relevant changes between the 90s and 2018?
* HTTPS has broken intermediate caches.
* Most responses are personalised or highly dynamic.
* Many responses contain PII or sensitive information and should not be cached.
* Many requests are rate-limited, billed or metered.
Observation: people go out of their way to cache static resources but tend to disable caching entirely on their JSON APIs.
I do have some bias; recovering pentester who rarely worked with APIs that served the same response to all users.
You can force some of it by putting everything behind TLS, but even then there will be downstream caches for some of your customers (especially the enterprise customers with their sweet, sweet enterprise money) which will still interfere with your expected caching.
Agree when it comes to the difference between POST/DELETE/PUT/etc. but almost every time I've seen somebody doing a GET which mutated state somehow, the people doing it were always dismissive of the importance of adhering to HTTP semantics and it always inevitably came back to bite us in some way.
It was sometimes caching related but there are a lot more things that can go wrong than just that.
Using POST where GET should have been used hasn't caused horrendous bugs in the same way, but it still introduces annoyances.
In my experience, "using POST where GET should have been used" is normally a consequence of URL length limits and HTTP not supporting request bodies for GET. I've never understood the latter decision.
REST is about hypertext-driving[0] and essentially nothing else.
[0] https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypert...
The very first comment from that post asks what he means by "hypertext", a fairly fundamental prerequisite for following his thesis. He replies with the following:
> Hypertext has many definitions
> When I say hypertext, I mean the simultaneous presentation of information and controls such that the information becomes the affordance through which the user (or automaton) obtains choices and selects actions
I challenge anyone to transform something as simple as "hypertext is text with links to other text in it" into a more convoluted and unnecessarily abstracted definition as the above.
Moreover, this is blog post and comments on a blog post: academic juries aren't the intended audience.
What's unclear or abstract about that? To choose an action, the agent must know what actions are available and how to call them. Whatever presents that information to the agent is the affordance. Fielding says that in hyptertext, you get that as part of the actual content as opposed to via documentation or some other channel.
> I challenge anyone to transform something as simple as "hypertext is text with links to other text in it" into a more convoluted and unnecessarily abstracted definition as the above.
Because that's not what it is, in general. See Xanadu for an example of vastly more general kinds of linkages than references.
The purpose of very abstract, generalised, academic (also legal) language is to attempt to achieve perfect technical correctness in description. Unfortunately this usually comes at the cost of understanding.
"Text with links to other text" obviously won't include 100.000% of applications conforming to RESTfulness, but it does describe what RESTfulness intends far better and more accessibly than a convoluted explanation, which very effectively serves the purpose of informing people calling RPC REST that it is not in fact REST (for example). It also gives a broad audience a general direction to work toward if they intend to use REST (or a reasonable argument not to use it if it's not what they feel they need).
The accessible explanation serves an actual practical purpose, the pedantic explanation has no actual benefit. The fact that you had to reference a project as esoteric and dead-end as Xanadu is testament to this.
But then that would be wrong. You had to know, through documentation and other out-of-band education, what you’re supposed to do with these `<a href=...` things. Ditto for any of the other options a page might present.
When you first used the web, you didn’t know what href meant. You had to read some instructions, or depend on UX designers’ decision to graphically represent a-tags in a way that suggests clickability and followability. That’s already outside his REST model.
Fielding is not being clear on how REST’s assumptions about “what you already know out-of-band” differ from other models.
Except text+links does not encompass the full meaning of hypertext. Forms and inputs are also possible, for instance. The more general definition you cite encompasses all of this (information+controls), and more possible innovations than your very specific definition which lists only the current ways.
I always find myself sceptical of people who appear to hold an almost religious level of belief about some programming construct or protocol and yet are unable to express themselves plainly. It comes off like a developer-ish version of business speak and isn't generally useful for moving a discussion forward or solving any problems.
Any term this confused just isn't useful.
"Monad" seems to be similarly confused, but there's a concrete math concept to fall back on, and concrete definitions in various languages. REST doesn't have that. No matter where I try to stick the pin in the landscape and say "That is REST", I am reliably informed by half-a-dozen people that no, that's not it, it's over there... and I get six different "theres".
(No, the thesis does not define it, in practice. Even when people reference it, they still point to different places! And besides... so a guy sat down and wrote a thesis paper on a word... that's great and very nice for him, and not worthless, but let's not go overestimating how much claim that gives you to a particular word. I certainly don't grant it enough claim to overcome the fact that still nobody knows what it means.)
Useless.
(I've carefully written this to be clear that I'm speaking about the term. Whatever $YOU think REST is may very well have good ideas in it, but labeling it REST only makes things worse.)
In most cases Monad goes the opposite direction than REST: most people discover that yes, what they've been writing is a monad, they just didn't know it. Conversely, most people are told that no, they are not doing REST right, because REST is actually about <something else> ;)
There's large companies calling their MVP "MVC", and then you have to explain that "MVC solves the problems of MVC" (-> real MVC solves the problems of Apple MVC, and is being rediscovered all the time with "unidirectional data flow" of things like React).
And then there's "React", which doesn't actually have anything to do with "Reactive", but has similar enough marketing that you think it might.
Which gets us to the current champion of muddled terminology: "Reactive Programming" or "FRP".
So although the various "reactive" (Rx) approaches are often called "FRP" (Functional Reactive Programming), they actually are not at all the same thing. At least according to the person who came up with FRP and coined the term. Of course, he no longer thinks that was a good name for what he did, preferring "Functional Temporal Programming".
And that's a good point, because as Backus points out (see https://news.ycombinator.com/item?id=17564186), FP doesn't handle time well, and so Functional Temporal Programming tries to address that deficiency.
Does that mean that the others are off the hook in calling their stuff "FRP" or "Reactive"? Nope, as it still doesn't make much sense, "reactive" being a description of systems not of implementation technology, and the implementation technology being a form of (synchronous) dataflow that can be combined with either imperative or functional languages.
So there is nothing essentially functional or essentially reactive about this stuff, and so when you add Rx or "FRP" to a non-functional language, you are adding a somewhat convoluted way of expressing dataflow, convoluted because of the unnecessary detour via FP. And while it does add the ability to build reactive systems to FP languages (which otherwise lack this ability), it does not add reactivity to non-FP languages.
And although the word "reactive" and the language used to describe these systems suggest high performance, most "FRP" libraries add about an order of magnitude of overhead...so they are slow.
Compared to that, REST is straightforward and well-defined.
Purity is useful in places, but serves no purpose if complicated enough that I can't explain it to my team.
FWIW React is a nice enough library, JSX is the first and only angle bracket UI design system that I like (sorry XAML), and it all works well enough with comparatively little ramp up.
I'll let other people argue what the proper abbreviation to describe my code is.
There are two hard problems in computer science: cache invalidation and naming things. Yes, and off-by-one errors.
Yes, you can just build stuff and not worry. And that can work out fine. On the other hand, muddles like these sow confusion and make sure you can't figure out how things hang together.
That's just using HTTP.
If you're doing caching, you want to do that throughout the network (via proxy) so you can have tiered control of traffic flow. If you're going to block access to networks, you want to do that at a firewall so you can do some access control independent of your API endpoints. If you're designing your API, you want your HTTP method to reflect the kind of operation happening on the server, so you can categorize traffic by method when you're looking at service metrics.
These aren't just "using HTTP well", they're deep architectural decisions. They deal with HTTP, but you make them not just because you're trying to use some transport protocol the right way, but because it improves your quality of service.
Because HTTP error codes are fixed and have well-defined meanings for HTTP that may not map well to your application. Repurposing HTTP error codes conflates your application-specific errors unless it maps to an exact subset of one of the predefined errors.
> Why create your own authentication mechanism when you can use HTTP authentication over TLS (or client certificates, or etc.)?
Do you have a client cert? I've never encountered any server that supports client certs in my entire 15-year career. Also, TLS is not a part of HTTP and HTTP only supported Basic Auth until fairly recently.
...and beware even the errors that match exactly! I've seen multiple systems in the wild that treat 404 as an application-specific error for "content not found". It sounds perfectly reasonable at first, but it puts you one misconfigured reverse proxy away from inadvertently broadcasting to the world that all your content is gone.
This can have data-corrupting effects. If an external system deletes resources via a reliable message queue and treats 404 as a success condition, they run the risk of dropping these messages and creating an inconsistent state. I've seen it happen.
Keep application-specific error messages distinguishable from the 'plumbing'.
If a system treated 5xx responses as successful, you'd consider that a problem with the system's response handling, not a problem with your own communication of state. So why consider abuse of 4xx to be a problem with the communication protocol?
This would reflect an incorrect design on the part of the external system.
For example:
PUT /queue/_hyn3
=> {"name": "_hyn3"}
GET /queue
<= {"name": "_hyn3"}
... processing ...
GET /queue/_hyn3
404
404 is not a success condition; it's also not necessarily a permanent error.Queues might be "reliable", but the consumers should never be assumed to be, and so it's good to have consumers provide a "complete" state update somewhere, and ideally, if the queue is able, for the queue to restore the incomplete item if the consumer fails to complete it in a specific (lengthy) period of time, because the consumer itself might have ceased to exist or failed for some other reason.
If you need to ensure that each message is consumed once and only once, you need to ensure that your queue also has something like a "completed state":
PUT /queue/_hyn3
=> {"name": "_hyn3"}
GET /queue
<= {"name": "_hyn3"}
... processing ...
PUT /status/_hyn3
{"name": "_hyn3", "status": "completed"}
GET /queue/_hyn3
404
GET /status/_hyn3
{"status": "completed"}
Obviously, there's an infinite number of ways to handle the semantics here, and certainly REST is not constrained in any way to just JSON, and perhaps you would prefer to put completed in the URL, but the point is that the architecture needs to support such a use case. TL;DR: don't rely on 404 as a permanent error, but it's absolutely appropriate for a document/content/file not found, even if the content will be found later or was found earlier.Client certificates are very common for API authentication (well-known examples: Kubernetes, Puppet, Saltstack, service meshes like Envoy and Consul...)
I'm always surprised by the resistance to client certs and all the gleeful usage of pre shared keys I find. OWASP top ten bad ideas for as long as I've been writing software.
this self signed certificate now has to sign the client certificate(s). Otherwise, the clients aren't allowed to address the master.
At least thats how i've come across it. Its actually pretty common in the infrastructure world. Your OPS team can probably tell you which services utilize it in your software stack, though you as a developer probably never had to worry about it.
Though a lot of services only use a singular client certificate across all nodes and just revoke the hole chain for rotations
* remote docker daemon execution
* setting up a kubernetes cluster with kubeadm and creating certificates for remote kubectl execution (this one might be related to the previous one. I'm not sure as i don't know anything about the kubernetes internals.)
* rabbitmq cluster iirc. At least thats how you're supposed to set it up with the sensu monitoring framework
* previously mentioned puppet does its as well, though the process is mostly invisible to the user
i'm not sure how its supposedly possible to spoof though?
the master needs to sign a certificate for clients. IF the master is compromised, everything is compromised. Thats true no matter which authorization protocol you're using
The kubernete's document says:
For an identity provider to work with Kubernetes it must:
1. Support OpenID connect discovery; not all do.
2. Run in TLS with non-obsolete ciphers
3. Have a CA signed certificate (even if the CA is not a commercial CA or is self signed)
I interpret it as saying you should perferrably use a real CA or a well-behaved internal intermediate CA. Anybody remotely familiar with how TLS works will probably tell you the same. If what you are saying is correct, it's a serious problem in our industry.You only need a 'real' CA if your service is public facing.
Heck,even a Windows AD server uses self signed certs
/Edit: after re-reading your comments I get the feeling that were really talking about different things. The self signed certificate becomes the base for your chain of trust. Everything signed by this is allowed to talk to the master. This verification happens on both sides. A client getting an unexpected certificate will refuse to talk to the master just as the master won't talk with a node, unless it's got a signed client cert.
Because the master cert is self signed, it's easier to just throw everything away and recreate the hole chain of trust instead of bothering with revocations.
Finally, there is no benefit in using an official CA if you are in control of everything accessing the resources.
Am I making sense now?
The master (this is the self signed part) cert becomes part of the client certs by signing.
These client certs are now only valid with the original self signed cert.
It's essentially a CA, only for the client certificates. It just isn't formalized because it's never imported. you'd also lose the ability to rotate the certs quickly without gaining any security by importing this self signed certificate as a CA.
TLS client certificates are strictly better than passwords because they don't provide an impersonator with a wildcard (and if you are running over the internet, especially with mobile devices, then you can get into that situation w/o being subject of a targeted attack). There are fairly few ways to achieve that other than client certs (SRP and SAE come to mind, both of which have virtually no deployment).
The HTTP layer is a very popular place to build applications, but it's fraught with problems and underspecification (or an impossibly large number of poor implementations: take your pick).
The number of TLS guides that recommend ignoring server certificates, or don't check CRL, or don't check certificate-transparency, or don't log and whitelist changes to the public key -- seem to have no end.
In the banking Enterprise, getting whitelisted on the HTTP proxy is infinitely harder than getting a port open.
And so on.
If you control the client, then there's absolutely no reason to use HTTP: Getting security, reliability, and redundancy on HTTP is extremely complicated, to which end I recommend people simply don't use it: Pre-shared certificates and smart clients are much easier to get right. You wan't caching? Specify and implement the strategy yourself (ideally using content caching instead of timestamps or some arbitrary tag).
If you don't control the client, treating HTTP as a tunnel for a protocol with pre-shared certificates and smart client (e.g. OAUTH1a instead of OAUTH2) is still better. You wan't caching? Assume cache-infinity and build cache-busting into the protocol. HTTP clients will lie about the correctness of their caching strategy often enough to spoil any benefit a "browser-side" cache might bring.
And REST?
My advice to developers is to ignore it.
When you say "why develop your own caching strategy", I say, because you have to anyway, otherwise you're leaving clients in the dust.
Why implement authentication? Because you have to anyway: Passwords/basic authentication are insecure, and everything else requires so much attention that you don't want decades of deformities around HTTP implementations getting in the way.
Error protocols? The error protocol is how you deal with it: Do you retry? Pause and let the user fix something? Alert the operator? HTTP proxies are notorious for modifying error codes so no matter how hard you try to signal in the HTTP stream, you're still in for pain.
What about ensuring that your desired ports are opened and your connection won't be eaten by a random firewall because you happened to pick a closed port and the user doesn't want to be bothered with firewalls and your application suddenly is crap because it can't even get its network connection to work when every other alternative works out of the box?
That's not a problem home-users have: They NAT.
Meanwhile in the enterprise, 80/443 is frequently locked into a particular (cheap) HTTP proxy server that can't manage basic features of HTTP correctly.
Actually, it is.
Furthermore, I'm assuming you don't expect applications to work only in the confines of a lan.
There are strong reasons why HTTP is highly favoured for communication protocols. Heck, whole industry protocols have been scrapped in favour of HTTP-based ones for this problem alone. See RTP and why it has been abandoned by HTTP-based protocols such as MPEG-DASH, and why the industry made it their point to develop a whole new video streaming protocol based on HTTP.
I'm saying almost all home-users have NAT and can communicate to remote services on most high ports.
If you're claiming otherwise, you should be able to provide some evidence to back that up.
> There are strong reasons why HTTP is highly favoured for communication protocols.
Yes. Like where they don't control the client, as in the part right after the part you quoted.
What exactly is your point?
Sorry, that's patently false and flies in the face of reality.
>If you're claiming otherwise, you should be able to provide some evidence to back that up.
You can start here:
https://news.ycombinator.com/item?id=17569715
> Yes. Like where they don't control the client
That is patently false. The problems isn't caused by not controlling the client. The primary problem is that they don't control the network, which means each and every single node between client and server, and HTTP reduces or eliminates the chance that one of those nodes doesn't cooperate.
As the author of perhaps the fastest dynamic HTTP server, I find this part particularly funny.
> informally-specified
Oh and this part. Anyone who thinks HTTP is well-specified and correctly implemented probably should be kept away from a computer.
I've also found focusing on the resource/noun
aspect ( ... ) to be a bit of a "nerd trap";
Oh, absolutely. Ten years ago, during one of my first projects involving a REST implementation, I was working with a person who I deemed fairly rational, and reasonable to work with.Coding up the REST paths was a breeze, and I had implemented like 99% of perhaps a month's worth of work in under a week. I laid out some quick docs for how to interact with the URL patterns and, proud of how quickly the whole thing snapped into place, I showed it to the guy.
He immediately jumped all over me, complaining loudly that the URLs "were not RESTful!!!" Which, of course, shocked me. Was he joking? Certainly not. This was a very serious error.
Well, okay! Big deal!
So I spent 15 or 20 minutes performing search and replace, changing words from things like:
/blue/104
/read/9008
/fetch/18
To things like: /car/104
/book/9008
/page/18
And wowee, crisis averted. I was still weeks ahead of schedule, and the commit was all of 10 minutes ruminating over preferable synonyms to please the sensibilities of humans, rather than struggle with computational correctness.During this time, I briefly mulled over the idea of trolling him with a many-to-many look-up table, and introducing variable synonyms that include opaque MD5 hashes of my original path names, and produce the same effective resource, such as:
/48d6215903dff56238e52e8891380c8f/104
/ecae13117d6f0584c25a9da6c8f8415e/9008
/5374034a40c8d6800cb4f449c2ea00a0/18
...but I otherwise enjoyed working with him, and didn't want to make enemies.The pedantry and neurosis some people raise over obsessive semantics is painful to behold.
Then again, if humans are part of the system, it's also probably a good idea to use more reserve with coworkers than "jumping all over me" implies.
What the article is saying, and what I think GP is saying, is that trying to model (non-CRUD) actions as "resources" in the pursuit of being "correct REST" is often a waste of time. `/nouns/8000/verb` is somewhat obvious to me (it does `verb` to `noun` 8000) despite not being "proper REST" whereas `/verb/8000` doesn't (what exactly am I `verb`'ing?), `/green/8000` would just make me scratch my head, and `/9f27410725ab8cc8854a2769c7a516b8/8000` tells me I'm wasting my time with this API and should go do something else.
As a user or developer I think being obvious and usable is more important than being "correct REST" and so I don't even use the term anymore, I just say HTTP API.
You might think it is just "pedantry" or "semantics" but consider that you are not the only one who will be using or reading these APIs, and design accordingly.
(However, if your coworker really was jumping up and screaming loudly at you because of this, this suggests he may not be as enjoyable to work with as you think)
Must everything be left-to-right? What if the nouns and verbs were not English words? What if the natural order of possessives or adjectives is reversed in a romance language? What if the words are not even cognates, and the characters to express terms are not even letters, but opaque pictographs?
The paths chosen are an abstraction, and REST is designed such that it should be easy to bind any one resource to perhaps multiple URLs. But honestly, to the vast majority of the world, these URLs never see the light of day, and every consumer of a REST URL is probably going to do so programmatically, perhaps once, while reading the docs and setting up tests, and then they'll call it a day, and never look back, because honestly, consuming data from API endpoints isn't actually anyone's idea of "fun" despite wild claims to the contrary.
For the sake of getting shit done, I usually avoid this conversation in practice, but honestly, also because arguing is worthless. People have their opinions as a matter of convenience to their own efforts, and not as considerations to others.
These are the tastes of a given developer creeping in as a leaky abstraction of human readability.
Where I do have trouble is specifically with error codes. There never seems to be the right one. Returning any frequency of 5xx error codes could give any load-balancer a reason to remove the instance and replace it. Returning far too many 400 codes makes many errors difficult to distinguish and often the cause is shared between the client and server and could succeed later, so would be inappropriate.
The main source of difficulty is where codes describe conditions rather than subsequent actions. It's better to think of 429 as the client shouldn't reissue the same request without backoff. This handles both rate-limited requests as well as a server being overloaded. In this case all is well. Status 400 tells the client not to reissue the same query--it's not clear if that only refers to the lexical structure of the query/body or also the server state, in which case 409 may make more sense. I don't have a clear understanding of what other codes mean to clients or servers.
When should one use 410 Gone:
"The 410 response is primarily intended to assist the task of web maintenance by notifying the recipient that the resource is intentionally unavailable and that the server owners desire that remote links to that resource be removed."
These codes were created for HTML and not REST in mind so we'll have to make do, but don't think they're complete or perfect as-is. New status codes keep getting created with varying adoption.
Here's a quote from a 2008 blog post:
Apparently, I use words with too many syllables when comparing design trade-offs for network-based applications. I use too many general concepts, like hypertext, to describe REST instead of sticking to a concrete example, like HTML. I am supposed to tell them what they need to do, not how to think of the problem space. A few people even complained that my dissertation is too hard to read. Imagine that!
Yeah, Roy. It's called being an effective communicator. Effective communicators include stories and concrete examples to help bring the audience along with them, instead of lengthy discussions of abstract concepts. Readers shouldn't have to perform exegesis on your slideshow and blog to get even a vague understanding of what you're trying to get at.
To be clear, any difficulty with REST in practice is a direct result of an underspecified concept without a clearly defined RFC as it relates to HTTP. Fielding didn't define anything about REST in terms of HTTP. All of REST, as it relates to HTTP, has been an application of what other people think he meant.
Nowhere in Fielding's 2002 academic paper[0], his 2008 talk at ApacheCon[1], or his blog[2] does Fielding have clear examples or thoughts on the kinds of specific details that Web developers often struggle with.
Fielding, in my estimation, seems principally concerned with three things with regard to REST:
1) Every important resource has its own URI.
2) All interactions against that URI should be stateless and cacheable: all application state that is specific to the user should live on the client (e.g., local storage for browsers), and all shared state should live on the server.
3) Interaction with that URI should be the same regardless of client (browser, mobile app, etc.).
Everything else is left as an exercise for the reader.
[0] https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
[1] https://www.slideshare.net/royfielding/a-little-rest-and-rel...
[2] https://roy.gbiv.com/untangled/category/web-architecture
(Ironically?) I've found this same problem to be one of the best reasons to avoid WSDL/SOAP. Too much pie-in-the-sky waffle, not enough pragmatic real-life flavour.
I had to study it for quite some time before I had any idea what any of it was actually for. It was clearly designed with the mindset that it is more important to theoretically support anything imaginable, than to be pragmatic and helpful to today's developers. To put that in buzzword terms, it fails to honour the principles of YAGNI and KISS.
It's very much a product of the ivory tower, and there's a reason it's been almost completely abandoned in favour of JSON/REST.
That's kindof ironic to say in a thread about the endless debate about doing REST the proper way isn't it? SOA(P) is really about defining a service in terms of its external interactions in language-neutral descriptions for big applications with long-term maintainability. It's not about developer convenience.
All REST applications I've encountered try to shoehorn state changes into HTTP PUT requests and would have greatly benefitted by expressing these as explicit state transitions in a WSDL-like RPC model. None has made the slightest attempt to represent state via HATEOS and advertise interactions via hyperlinks, which is the entire point of REST and loose coupling. In typical server-side languages other than JS/node.js, JSON isn't a native object serialization format and must be produced/consumed exactly like XML (eg. by using binding annotations or similar). So I'm not sure what has been gained, other than a mess of JSON-over-http services without transaction boundaries for the next generation to clean up.
I've not used binding annotations when working with server-side JSON in Ruby or Python, and there's little need to do it in Java unless you want a mechanized description of your API data structures to do other things with, like documentation or tool-driven schema validation.
HATEOS is nuts for an API layer. It forces an overly chatty API because you need to navigate a graph to perform actions, rather than looking up paths in docs - but the paths don't go away, they just move from being path templates to resources to being path of navigation through a series of responses. The essential complexity is constant, and the need for hard-coding endpoint knowledge into the API client hasn't gone away, it just moved.
HATEOS makes some sense for user interaction, but even then it works poorly for people who fork their navigation: open up several child pages for a list of items, add each to the basket, then go to the checkout. Do they all go in the same basket, or do you have several different baskets depending on the path of navigation? HATEOS will guide you to the latter, because hypertext drives the state, rather than some hidden state. HATEOS works much better in a read-only context, where you're e.g. sorting and filtering through a list. Actions, less so.
He's optimising for communicating with his peers: scientists and academic researchers.
If you find this communication hard to digest, then chances are you are not part of the intended audience. It is then mostly pointless to complain or try to change his wording.
You can buy books from other people who deliberately target developers.
• https://oreilly.com/catalog/9780596529260 • https://oreilly.com/catalog/9780596801694 • https://restinpractice.com/ • http://www.designinghypermediaapis.com/
An author uses technical jargon and concepts familiar to his intended audience. Fielding's intended audience for his thesis is other people with PhDs. His writing was perfectly cogent if you have this level of knowledge.
The people who really failed REST are the blog authors who didn't have the necessary background to evaluate the REST constraints and started talking about REST despite not understanding half of the thesis. This is what diluted the term.
> Fielding, in my estimation, seems principally concerned with three things with regard to REST
You missed the central importance of hypermedia.
> Everything else is left as an exercise for the reader.
That's like saying an RPC library leaves designing and building your specific API up to the reader. Of course it does, it has no business imposing any more constraints than are needed to fulfill its purpose.
REST specifies some architectural constraints that entail certain distribution and scaling properties, that's its purpose. Anything beyond that is domain-specific logic.
Perhaps, yet his blog is written in exactly the same style.
Who are all these ComSci researchers who are most comfortable with these concepts and distinctions, anyway, and who are also designing web APIs? At some point, if you wish any real system to use your ideas, you need to communicate it in some way that explains it for a typical software developer. If you write a blog in the style of a thesis, only thesis committee members will ever want to read it, or be able to understand it.
It's like the old story of the plumber who discovered hydrochloric acid clears clogs, and when he reported it, some scientists told him "The efficacy of hydrochloric acid is indisputable, but the corrosive residue is incompatible with metallic permanence", and he thanked them. Repeat several more exchanges, with even more jargon. Finally, they tell him "Don't use hydrochloric acid. It eats hell out of the pipes."
More than you realize, I'm sure. There are plenty of industry people who can understand theses and other research publications. You can find them here, on reddit, on stackoverflow, and on lambda-the-ultimate.
> At some point, if you wish any real system to use your ideas, you need to communicate it in some way that explains it for a typical software developer.
That's what books and blogs are supposed to be for, but how many of these people do you think spoke with Fielding before writing about REST to ensure they got it right?
Fielding's emphasis on abstract concepts are because REST isn't tied to HTTP, or HTML, or XML, or JSON. He's trying to explain calculus to you so you can solve many problems, and you keep focusing only on how to calculate velocity given acceleration just because that's the problem in front of you right now.
REST was a nice idea when it got (re)invented about ten years after Fielding's thesis but it left too much open for debate, ambiguity, and nitpicking and quickly degenerated in different opinionated camps trying to retrofit new ideas to the existing practices around http, which mainly boiled down to interesting ways to do RPC.
Before HTTP, people were already long doing RPC using e.g. DCOM, CORBA,RMI, etc. Reinveinting RPC over X has been the fate of pretty much every way we've come up with to make two computers talk to each other.
You only need GET and POST for REST.
> Reinveinting RPC over X has been the fate of pretty much every way we've come up with to make two computers talk to each other.
REST isn't just RPC, it's RPC with specific constraints to enable caching, scaling, adaptability, and distribution properties that unrestricted RPC alone cannot achieve.
That's why REST is still important today, because RPC is too flexible, and RPC without constraints will inevitably run head first into all of the issues that REST solves for you. Unrestricted RPC libraries then let you "solve" these problems in myriad incompatible ways each with their own problems.
I mean, this is exactly the state of REST today. It's not specified well enough to admit general purpose clients (like, for example, GraphQL does) because it's just a "pattern" not a specification.
Because every API has its own way of doing paging, subresource expansion, expressing side-effectful operations, etc it means every API has a corresponding custom-built client.
> Caching
Do these custom clients implement http caching? Not usually.
> Scaling
Arguably, REST is responsible for bringing about the rise of stateless API servers. We should be grateful for the good idea, and not feel compelled to adhere to the other aspects of REST if they don't suit our purposes.
> adaptability
Claims of additional adaptability in REST are usually talking about HATEOAS where each resource defines how it can be interacted with by providing links and semantic metadata etc. I will flat out say it: this doesn't work. Short of building some intelligent agent into your API client, hypertext will never be the engine of application state in any meaningful way. You can't just change the links and have the client magically discover the right thing. Fielding's thesis was talking about the web, where humans are the ones deciding which links to follow. It's a useless concept in API design, and it adds tremendous overhead for zero benefit.
I don't think that's true. It's very well specified, it's just an extensible specification admitting many possible hypermedia formats. There is no one single hypermedia format to rule them all. Fielding's thesis was about the architecture that allowed the web to scale, and content negotiation and extensible content types are part of that.
> Because every API has its own way of doing paging, subresource expansion, expressing side-effectful operations, etc it means every API has a corresponding custom-built client.
This isn't necessary, depending on what you mean by "client". All such operations should be encapsulated as links embedded within a hypermedia format. The client needs only to know the high-level workflow or path to navigate the hypermedia and extract the links it needs.
The hypermedia format itself can change, the links can change, but as long as the path through the hypermedia is preserved, the client works. And there are ways to increase adaptability even further if you don't want to hardcode a path through a set of hypermedia documents, like a flat directory of endpoints (but it's similar to the path, since there must be some shared semantic knowledge about what you're looking for).
> Short of building some intelligent agent into your API client, hypertext will never be the engine of application state in any meaningful way. You can't just change the links and have the client magically discover the right thing.
It sounds like you misunderstand the use of hypermedia for autonomous agents. Throw out HTML and consider a hypermedia format that has only two node types: a named embedded link, and non-link data. An autonomous program can then enumerate all links, or access specific named links it's interested in that may be deeply or shallowly embedded within a web of hypermedia documents.
Most endpoints will probably be shallowly embedded, like with PWAs where the JS simply does a form of RPC with the server. But if you consider more complicated aggregated services, like interacting with someone's bank account where the number of services is large and managed by separate teams (mortgage, stock broker, chequeing, etc.), then it starts to make sense to require deeper embeddings to permit better organization of your teams developing distinct programs on the server-side.
The point being that REST gives you considerably more server-side flexibility to do this.
Every time you type hypermedia I want to punch you.
hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia, hypermedia!!
The key difference between Representational State Transfer and RPC, which makes all of the other capabilities of REST possible, is:
REST deals with nouns and uses HTTP verbs (the common ones, or you can invent your own) to handle a specific and common grammar of operations against those verbs:
POST _hyn3 > /user # (C)reate
GET /user/_hyn3 # (R)ead
PUT /user/_hyn3 # (U)pdate
GET /users < _hyn3 # List
DELETE /user/_hyn3 # (D)elete
RPC was literally "remote procedure call", which means you're calling a remote verb and then specifying your noun as an argument or data: POST _hyn3 > /users/create_user
GET /users/get_user/_hyn3
POST /users/update_user/_hyn3
GET /users/list_users < _hyn3
POST /users/delete_user/_hyn3
It's more than just semantics, because it gave a common language for almost all CRUD type operations. (Thus, even though REST was never about CRUD, as the titular article states, CRUD and REST do have a lot in common, because CRUD represents an extremely common data access pattern and common HTTP verbs, if not REST itself, map to it very cleanly and nicely.)In other words: REST deals with HTTP and the facilities that it provides. It doesn't directly with the verbs behind HTTP, such as GET/PUT/DELETE/etc. It instead provides an architectural paradigm shift, in which the URL itself is a discrete representation of the data in question, and the verbs -- RPC's -- are just standardized ways of dealing with that data.
This seemingly simple change, obvious only in hindsight, set the stage for a massive change in how API's are produced and consumed.
If you can present your domain as addressable representations of resources and if you can lay out your business logic as changes to these resources, then you could use REST via HTTP to provide access to your service.
Furthermore, if it is important to allow any client to connect to your service and you don't have any control over the clients used, then adopting HATEOAS provides a backward-compatible, future-proof way for client software to work with your service.
If, OTOH, you have full control over the client-side, you can trade-off these abilities for a simpler model (like GraphQL, if you must, or simple RPC over HTTP)
Know what you design for.
Re Caching: Caching in HTTP is very flexible. E-Tags basically give you fine-grained control over how to cache and allow for significant bandwidth/performance improvement. However, in many cases your app will also benefit from an additional cache layer in your persistence layer. Don't confuse them.
It makes for nice code.
Promise<JSendApiResponse>
With the benefit of HTTP level stuff not been mapped onto application level stuff.I like KISS with this kind of thing and if you need a more complex solution it handles that as a starting point.
interface IResponse {
foo: string
}
async function doSomething() {
const reponse: AxiosResponse<IResponse> = await Api.call()
...
// or you can use AxiosPromise<IResponse> without the await
}This is precisely it. If an integration mechanism can't be implemented as a clear and uncomplicated library that strongly encourages correct use by making it the path of least resistance, it probably isn't any good.
(And note that I said "library" rather than "framework". This is important as libraries are more like a thin layer of glue while framework is more like a metastasising cancer)
Most of the time, REST, strictly speaking, demands too much attention even for relatively simple stuff, and therefore is not a good integration technology. However, some adaptations of REST are.
The one thing that is a bit sad is that there is some nomenclature confusion. These days a REST API doesn't refer to Fielding's vision, but a more practical adaptation of the mechanical bits of it.
It seems to me that the initial popularity of "REST" was largely because it wasn't SOAP (and didn't have to be XML), but it was still a name you could give what you were doing that made it sound like you were following some sort of standard.
It's not hard, but I see very few differences between them. SOAP is easier to read/edit, JSON has less overhead/data, and REST...well, nobody could decide what REST was, so they just made it up or ignored it.
Just curious, what programming language(s) did you use to provide and/or consume SOAP APIs?
Perhaps my own experience trying to use SOAP can shed some light on why REST prevailed. In 2004, I tried to use the PayPal mass payment API from a Python application (in the now-defunct Quixote web framework). As I recall, I found one Python SOAP client library, but for some reason, I couldn't use it to access the PayPal API. It was all just so complicated. In desperation, I ended up writing a small service (micro-service?) to access the PayPal API using the official PayPal Java SDK. That service used Jython, because I just couldn't stand Java's verbosity. The main application communicated with the service using XML-RPC.
Compared to SOAP, when I discovered REST, it seemed refreshingly simple, built for the Web rather than awkwardly abusing HTTP as a transport for an over-complicated RPC protocol.
Where there's a will, there's a way to 'do it wrong'. It's amazing the kinds of messes people can (or are forced to) dream up in this world... even if they're ostensibly playing by rules that aim to make messes 'impossible'.
When mutating state with GraphQL the suggested approach seems to be to have "action based" endpoints, i.e "publishArticle", which resonates well with me.
Moving flexibility to the client comes with all the inherent risk of that flexibility being available to (in a browser sense) a risky client.
RPC over REST is a solved problem IMO, arguments about REST RPC semantics become ideological after a while.
I'm interested in understanding more about this statement.
Do you mean that all REST RPC libraries are sufficiently developed such that you don't really need to worry about RPC being tricky, or that we all just as a community understand the best strategies and common pitfalls. Or do you mean a specific technology?
Just wondering cause whenever I hear the term RPC as an experience developer I just shudder (-: Thanks!
Ultimately we find ourselves building 80-90% CRUD operations with edge cases that don’t entirely fit with the straightforward RESTful toolset, and in those situations making a call on how to handle it in code is a topic that has by now been more or less discussed to death and becomes a stylistic choice.
REST isn’t a protocol, there is definitely interpretation at work.
I was using the term “RPC” to cover topics that are outside the scope of CRUD, “do a thing” as opposed to “manipulate a thing”.
My own personal take on RPC being solved is that we're all pretty much agreed it's not a good idea unless you absolutely have to!
(I mean RPC in it's more narrow definition as remote, synchronous function calls - e.g. CORBA, SOAP, XML-RPC or the original Unix RPC protocol - for me this is what makes the resource oriented approach of REST a much more appealing approach)
I'm very much of the opposite opinion — Ideally services are indistinguishable from any other library, and the fact that they're running remotely instead of locally only means there's a couple more (networking-related) error cases to handle. RPC setups like Thrift or gRPC support this way of thinking relatively well, whereas REST's resource/verb paradigm really forces your APIs to have a certain shape.
I don't mean to be dismissive [0] - but that mindset seems a bit prehistoric to me. We tried CORBA, everybody hated it, and it died!
Indeed, the bible on the topic [1] devotes substantial amounts of time to how to structure your interfaces just so that you avoid issues arising from the discrete system boundaries.
There is a world of difference between issuing an instruction that repositions a program pointer from one position in memory to another, and transferring execution from one compute node to another while artificially blocking execution to simulate a single thread of execution.
(Even, on a single machine with multiple cores this can be a problem - though to a much more limited degree)
This is why we prefer loosely coupled, message-passing approaches, and resource-oriented ones such as REST. This is why modern languages have "futures" and "promises" (the implication being that we sometimes break promises) and why functional languages are so much in vogue (specifying "what" and leaving a substrate more sympathetic to the underlying architecture to decide "how").
[0] https://en.wikipedia.org/wiki/Fallacies_of_distributed_compu...
[1] https://www.pearson.com/us/higher-education/program/Henning-...
"RPC over REST" is oxymoronic.
On the front-end, what are your thoughts around managing data in the in-memory store (Apollo Client)?
The author has left out a few key components of REST in their initial distillation. Critically:
> REST components perform actions on a resource by using a representation to capture the current or intended state of that resource and transferring that representation between components.
So given the example from the article:
POST /articles/{articleId}/approve
This URL does not point to a resource, and the HTTP verb does not describe what action is being taken upon a resource. It's also not using the representation of a resource to capture intent.It's also worth noting that a GET for the article would return the "status" field and presumably other PUT requests to update the article would either act directly on the article or have other vanity action URLs, making the API more verbose than if everything were done properly.
A RESTful design would be in essence CRUD. It would have a url (/articles/{articleId}) identifying a resource, it would have a representation of a resource that includes a "status" field, and changing the status of the article would involve a PUT request to the URL of the article with the desired change of state.
(Not even as originally intended, which is very different from RESTful way 99% use it -- originally REST was all about self-reporting and discoverability, which is not used, and irrelevant to most use cases).
Having JSON as the response, and a scheme to address entities, was good. Even using HTTP action semantics was OK (though debatable). But that's far from what REST is about.
Something like JSON-RPC would be a much better for web API needs.
I used to be partial to that interpretation, but after a few years of looking at it, at what people do and at what's actually valuable, I've come to the conclusion that coldtea is correct: REST as originally intended was not that good for programmatic contexts.
It certainly didn't help that it got quickly buzzwordified as "actually use HTTP properly instead of just shoving your garbage through POST all the time" (the original sin of SOAP or XML-RPC), but even then what makes "REST" work for browsers (which is what the concept was extracted from) is that there is an intelligent agent doing the discovery and driving the interaction.
That's not useful for a programmatic API. You can add a discovery layer to a programmatic API (as e.g. graphql schemas & explorer tools) but requiring programmatic clients to go through such a discovery layer just makes interacting with the API painful for no value.
Traversing a few pages when clicking around in your browser makes sense, doing so from a programmatic client when you could literally just store the correct endpoint and perform your call directly is nonsense: it's inefficient, it's annoying and it's pointlessly verbose. Yet that is essentially what proper rest as described and clarified[0] by Fielding require.
Even the "multiple interpretations" (multiple content types) option is worthless when browsers don't provide the flexibility required to do content negotiation[1] or leverage HTTP verbs beyond GET and POST.
[0] https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypert...
[1] even in places where they allow actual mimetypes it's as likely as not they'll ignore it e.g. object/@type is a mimetype but no browser I know cares about it, most of them send Accept: ∗/∗ and firefox sends its pagewise/default Accept.
That requires implicit knowledge of the 'correct' endpoint. If that endpoint changes or anything in between, you'll have to start versioning your API, which basically tells all existing clients: Sorry, you got to update.
Allowing clients to discover resources is a future-proof way to keep your client libraries compatible and evolve your API without versioning. It is more work for the client and it is not always suitable. But if you intend to design something future-proof (and we are talking about 10+ years), then REST has already proven to be very successful (your latest browser can still browse a web page from 15 years ago...)
In exactly the same way that the "RESTful" version requires implicit knowledge of the content types: that knowledge it not implicit at all, it is specifically and explicitly embedded in the system.
The word you're looking for is static.
> Allowing clients to discover resources is a future-proof way to keep your client libraries compatible and evolve your API without versioning.
That is completely untrue.
> But if you intend to design something future-proof (and we are talking about 10+ years), then REST has already proven to be very successful (your latest browser can still browse a web page from 15 years ago…)
Because it has a somewhat intelligent and flexible human driving it and able to adapt in changes to the visited targets. Not so with a programmatic client.
A discovery layer isn't necessary if you're using hypermedia as intended. "Discovery" just means a loosely coupled format associates the "logical" addresses, which your program shouldn't know a priori, with a readable name, which your program uses to find the logical addresses it actually needs.
It's like the difference between programming in assembly language with direct memory addresses (URLs), vs using a programming language that abstracts memory addresses with named variables (hypermedia format that associates readable names to URLs), thus decoupling clients from needing to understand potentially opaque URL formats.
The real problem with REST is that REST frameworks don't provide any guidance or support for any hypermedia format that isn't HTML, and HTML isn't well suited to programmatic interaction. None of the commonly used data interchange formats support distinguishing embedded links from other data, so they aren't hypermedia formats. No wonder doing REST right is so difficult.
> doing so from a programmatic client when you could literally just store the correct endpoint and perform your call directly is nonsense: it's inefficient, it's annoying and it's pointlessly verbose.
Except it's not pointless, it allows painless server-side upgrades. It might cost you some efficiency, but it provides significant flexibility.
You're absolutely correct.
It's not the misuse that made REST "never that good".
It was already not that good (again, in my opinion).
Just because users might be extra careful in their use of a UI, doesn't mean the UI is not badly designed itself.
A big red button that says "Click me for free lemonade" but instead burns the whole computer is bad design -- even if the users have read the manual, so they know what it does and don't press it.
GraphQL is a relational query language, just like SQL but only less powerful.
Fact is that people always wanted a just database anyway. But with some graphical interface on the front. Once we moved the interface to the client in an SPA, it seems unavoidable that backend will turn database-like.
So yes that's the exact analogy. We're writing databases on top of databases. Only ours are no-SQL.
I don't think this is accurate. Discoverability was the bonus score piece, the most fundamental parts were statelessness and caching.
A lot of grievances in early web development came from state breaking when people browsed back, reloaded pages, did other things between requests, or got hit by pretty aggressive caching.
Nothing intrinsically REST-blocking in the original zero JavaScript, form-based apps, they were just that messy and expected people to follow a linear path on a single browser window.
People where solving these problems for the first time (for the web), and they actually needed to be told to care for state.
During development with Rails I struggled a lot, because it was forcing CRUD ideology in places where the custom route could work (and adding it was so painful).
Standardization is fine, but usually it does not solve development issue, it solves management issue, which is common for big orgs not small ones. Especially when it gets on hype or forced by the top management, like it happened numerous times, with latest examples of React, GraphQL or Microservices.
Then the interpretation becomes the pattern, and it becomes a self-perpetuating fallacy.
class AsController < ApplicationController
def index
end
class BsController < ApplicationController
def create
end
end
end
with routes like resources :as do
resources :bs, controller: "as/bs", only: :create
end class AsController < ApplicationController
def custom
end
end
routes: resources :as do
member { get :custom }
endThe whole point of my example was that it is possible to do resourceful routing in a single file without having to resort to using custom routes. I try to avoid custom routes whenever possible - in my experience they lead to pain in the long run, more often than not.
Rails _might_ have helped, but I have come to enjoy Sinatra's flexibility. Been debating about making something in between, but working on other things.
Rules are NOT made to be broken, as you claim, and I see more people getting themselves into trouble for thinking that.
This is computer science, not a game.
In fact the rules you talk of are totally ad hoc and/or cargo cult. At best, they're self-reported empirical (pre-scientific) findings.
There's no systematic research that they're better than some alternative set of rules etc. People just adopt this or that framework or methodology (like REST) because some famous programmer wrote it or suggested it, some company promoted it, it picked up steam of GitHub and blogs, and so on.
That's not only not "computer science", it's not even "software engineering".
This reminds me a bit of my first experiences with Common Lisp to write anything resembling serious code. (A Java compiler for my compilers class in school.)
My education to that point had almost entirely been driven from a purely list based and functional view of the language. Instructors would default to lists at the expense of other data structures and strongly encourage 'non-destructive' operations at the expense of mutation. (Even the term 'destructive' carries with it an obvious negative and somewhat dangerous connotation.)
Flash forward a couple semesters, and I'm trying to take this approach beyond writing simple unifiers and minimax search and apply it to a compiler with multiple layers of code that's being built up in phases over months. A month or so in, my resolve cracked, I started exploring some of the other options provided by the language (hash tables, vectors, mutation, etc.) and made a bunch more progress (and got something working by the end of the class).
My point in saying this isn't really to advocate for mutation, etc., but rather to agree with your point that sometimes it's worth breaking out of self-imposed constraints so that you can face the external constraints that are probably more important. (ie: my business client probably doesn't give a damn if my RESTful interface is RMM level 2 or three as long as it lets her effectively run her business.)
(One thing I'll add about the Lisp experience above is that it was more than half a lifetime ago... I'd probably be more nuanced with my use of mutation, etc. now than I was then. But then, I was much less experienced and a refugee from Pascal, etc. One of my constraints was my instructors' encouragements to avoid mutation... but another of my constraints was my own lack of techniques for working that way.)
When we use REST as our layer to convey the API itself, we really lock ourselves down to a very specific set of URLs and HTTP Status codes. I'd rather have a rich error object thrown, not an HTTP status code that I have to examine.
I've had a pretty strong disdain for REST for the last few years and have been actively finding replacements. The closest I can come up with is Grpc. I'm dying waiting for Google to finish up Grpc-web however. If we continue to tie ourselves to a bunch of server resources by URL we're going to take forever to get off this archaic system that has no long term future.
Thinking in terms of REST requires that you concern yourself about the full infrastructure. Many programmers i have met simply do not care. Super nice properties such as cacheable and discoverable are outside their domain. Most simply just want remote RPC.
Try convincing a programmer of the benefits of HATEOAS is though. It will be dismissed as too much hassle with no real benefit.
But if you look at it from a data architectural perspective everything just "clicks". Few projects are however approached from that angle.
It is a little unfortunate that we don't have a CREATE verb (distinct from the update-ness of PUT) and we need to use POST for this purpose.
POST is a call to do something with that data, which might include changing the data. Off the top of my head, logging in, actions with external systems, sending emails.
I'm sure somebody will reply saying "no it clearly means this!" or "it obviously means using HTTP how it was supposed to be used" or "those other people just don't understand REST", but the fact is we still have articles - in 2018! - that are trying to explain exactly what "REST" is.
When resources are obvious nouns already present in the backend's data model, this maps well to CRUD. The problem comes when trying to describe complex mutations, because to most people, this feels wrong, instilled by years of Dos and Don'ts of half-baked lessons about Object Oriented design.
In all this time between when Roy's thesis was rediscovered to promote an architectural style and when the trendiness of REST has finally run its course, there were far too many posts complaining about deployments missing hyperlinks (when usually, the API was externally documented, URLs templated, and its clients hardcoded, making many usecases of embedded hyperlinks moot), and not enough to reassure flummoxed designers that naming resources after verbal nouns or deverbal nouns is perfectly fine. Oops.
- Providing a block of data, such as the result of submitting a
form, to a data-handling process;
Which fits with much more than mere CRUD. And it's specifically written that it doesn't have to result in the creation of a new URI-addressable resource.(Also, REST isn't HTTP, you can comply with the former even if you're violating the latter, and vice-versa)
Exactly. Complex processing via POSTs instead of distinct URI-addressable resources is HTTP-compliant but not REST-compliant.
- HTTP verbs are like normal verbs. They represent an action.
- URLs represent the location of a resource or collection of resources.
- Resources are like nouns. They represent what is being acted upon.
As soon as you start assigning different actions based on different URLs, the HTTP verb no longer actually represents an action and the URL no longer points to a resource.
A RESTful architecture needs an Uniform Interface, yes, but their it's doesn't have to be an "action" or anything specific. POST has a definition, and everyone handling the requests can rely on it. That fulfills the constraint.
It's not REST, it's just HTTP.
POST /articles/{articleId}/submit
POST /articles/{articleId}/approve
POST /articles/{articleId}/decline
POST /articles/{articleId}/publish
I find this troublesome because the URL path component then includes a verb, an action, so there are two verbs in the same sentence!This can be adjusted to:
POST /articles/{articleId}/submitted
POST /articles/{articleId}/approved
POST /articles/{articleId}/declined
POST /articles/{articleId}/published
Now the URLs really represent resources on which HTTP verbs operate, and you can readily guess what GET (if needed/applicable) on each resource would give you.There is some cognitive dissonance introduced by the inconsistency of abstraction levels between the former "RPC style" (where HTTP verbs are mere actuators† and the intent is in the URL and/or in the payload), and the latter "REST style" (where verbs carry intent acted upon resources).
EDIT: I do not advocate for one over the other, merely that an API should be consistent overall and not mix both.
† They are used as pure HTTP feature control (like caching) and do not carry application-level semantics.
I'd like to hear about guidelines, like for that transaction workflow example, how would you implement this? What would be your data model?
That font in the titles could have been gorgeous, but that upper case "D" is offendingly bad.
"Search my dissertation and you won’t find any mention of CRUD or POST." [0]
> For example, some APIs may need to support actions such as submit, approve, and decline. With our somewhat limited action verbs in HTTP, how can we add this to our API?
By representing the difference in states. Or otherwise. The communication medium is not the API. ("A REST API should not be dependent on any single communication protocol, though its successful mapping to a given protocol may be dependent on the availability of metadata, choice of methods, etc. " [1])
> POST /articles/{articleId}/publish , POST /articles/{articleId}/submit , ... etc.
A few years ago I was zealously advocating against representing actions in URIs, and using a generic /actions [sub]resource, but this actually makes it simpler to represent the available actions to the client. And sure, this can be thought of as REST via the code-on-demand part of the architecture. The server points to a script (it doesn't have to be JS, but the media type must be defined in the REST API), that can understand the available actions from the state representation, and construct the necessary representations for the desired actions. (So in a HTTP API, the JS interprets the JSON and knows how to put together POST requests to modify the resources, without the actions having been reified and URI-fied in the JSON.)
> The workflow our API supports is more explicit, [...]
Sure, that's pragmatic, but after much gnashing of teeth and scratching of heads, REST is about presenting options to the client by offering various media types (content-types), and letting the client request the best representation, and not really about a fixed ACL neither about fixed flow.
From [1] again: "A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types. Any effort spent describing what methods to use on what URIs of interest should be entirely defined within the scope of the processing rules for a media type (and, in most cases, already defined by existing media types). [Failure here implies that out-of-band information is driving interaction instead of hypertext.]"
[0] https://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post
[1] https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypert...
Arguably this is the problem: what Fielding is describing here has a well-known name: Object Oriented Analysis and Design. It's not at all clear why a new formalization was needed when we already have Objects. Resources have URIs, Objects have memory addresses. Resources have representations that are defined by content media types, Objects have behavior that are defined by -- types.
> POST /articles/{articleId}/publish , POST /articles/{articleId}/submit , ... etc.
Good ol' OOAD can avoid nonsense like this. It's immediately clear that the Resources identified by these URIs aren't real objects. If you want to talk Published Articles vs Submitted Articles you would introduce new types and new repositories.
The only way this makes any sense is:
POST /articles/published/ POST /articles/submitted/
Only now does 'GET /articles/published/{articleID}' make sense and we might think about what that content media type looks like vs submitted articles.
And of course, the problem is that nobody sits down to just do a quick World Wide Web API for an app, or an internal corporate business-as-usual thingie. In fact since the original hypertext project at CERN no one every did it. We still use the same Web, incrementally transformed into what it is today, but still the same HTML, same Content-Types, and HTML1.0-like markup. All eaten and excreted by JavaScript of course, but that's a prime example of something incremental, and not exactly thought out.
Moreover, there's not much to say about media types driving interaction, because 99.9% of the Web is GET GET GET. And OPTIONS (due CORS) is driven by out-of-band conventions (defined in the Fetch and the amended XHR APIs, but not much to do with media types - but of course, CORS is more about fixing the leaky encapsulations of the communications interface/layer, not about the content [the high level state being transferred]).
yeah the perfect rest protocol is the web and the perfect rest clients are the humans that navigate the action represented on the hypermedia.
that said, there's some value to be had in discoverability, even if rest grossly gloss over how to discover parameters for the action that the state holds.
There absolutely is and API systems like graphql have taken that to heart (playing with a graphql explorer is delightful).
But forcing all programmatic interactions through a "discoverability" layer is not useful, yet that's essentially what REST (as originally formalised) mandates.
That is a lot of thinking for something that matters very little in REST. URI for actions are embedded in the response itself anyway. If you aren't using the actions as provided by the response, you're not doing REST.
>By representing the difference in states.
Maybe I'm misinterpreting something here, but doesn't this directly contradict the following?
"A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). Servers must have the freedom to control their own namespace. Instead, allow servers to instruct clients on how to construct appropriate URIs, such as is done in HTML forms and URI templates, by defining those instructions within media types and link relations. [Failure here implies that clients are assuming a resource structure due to out-of band information, such as a domain-specific standard, which is the data-oriented equivalent to RPC’s functional coupling]."
How do you implement the above without making out-of-band assumptions about the name/structure of each action state (e.g. /publish, /submit)? The server-provided links won't help you there, since they might as well be called /foo and /bar - after all, the server provides them and the client shouldn't have any prior knowledge about them.
I mean, sure you would associate each action state (/publish, /submit) with a media type, but that's just shifting the assumptions elsewhere, isn't it?
I believe Roy quit visiting forums for those reasons; a frustration dealing with all that.
Those people indeed know what REST is -- in fact, they define what REST is. When we talk about REST, we talk about how people use it.
They might not know what "REST as defined by Dr. Fielding is" but that's not really relevant to them or to the industry at large.
use: the action of using something or the state of being used for a purpose.
usage: the action of using something or the fact of being used: a survey of water usage
Those people indeed know what USAGE is -- in fact, they define what USAGE is. When we talk about USAGE, we talk about how people use it.
They might not know what "USAGE as defined by coldtea is" but that's not really relevant to them or to the population at large.
:-)