> The “benefits” REST is supposed to introduce
Is one of the more egregious straw men I’ve seen in a while. Never once in my twenty years have I heard anyone say it’s nice because “you don’t have to read the docs” or “it works in a browser”
REST helps with lots from thinking through clean data models to helping ensure consistency within an API.
> "A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media types that are appropriate for the intended audience (i.e., expected to be understood by any client that might use the API). From that point on, all application state transitions must be driven by client selection of server-provided choices that are present in the received representations or implied by the user’s manipulation of those representations. The transitions may be determined (or limited by) the client’s knowledge of media types and resource communication mechanisms, both of which may be improved on-the-fly (e.g., code-on-demand). [Failure here implies that out-of-band information is driving interaction instead of hypertext.]"
- https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypert...
The missing piece here is hypermedia, which is a requirement for a REST-ful API, as Fielding notes again with frustration in the same essay.
[1] - https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_st...
I've seen multiple people argue these points. shrug
This.. isn't true?
None of my browsers seem to have PUT, PATCH, and DELETE baked into the standard UI (or even POST really), so maybe it is true?
You might use a REST client instead or what some services do is offer a playground coded in JS to play with the end point but such a playground needs to deal with CORS.
What they seem to mean is that you can't access most of them via the GUI directly, which is true, but their "solution" is to make queries and commands both use POST, which means they've now thrown away the ability to even access read-only endpoints via the GUI.
I'd like to just leave a few links on REST here for the reader's consideration:
https://htmx.org/essays/how-did-rest-come-to-mean-the-opposi...
https://intercoolerjs.org/2016/01/18/rescuing-rest.html
https://htmx.org/essays/two-approaches-to-decoupling/
https://htmx.org/essays/hypermedia-apis-vs-data-apis/
https://htmx.org/essays/hypermedia-clients/
https://intercoolerjs.org/2016/05/08/hatoeas-is-for-humans.h...
https://htmx.org/essays/hateoas/
In addition, I’d just like to interject for a moment. What you’re referring to as REST, is in fact, JSON/RPC, or as I’ve recently taken to calling it, REST-less. JSON is not a hypermedia unto itself, but rather a plain data format made useful by out of band information as defined by swagger documentation or similar.
Many computer users work with a canonical version of REST every day, without realizing it. Through a peculiar turn of events, the version of REST which is widely used today is often called “The Web”, and many of its users are not aware that it is basically the REST-ful architecture, defined by Roy Fielding.
There really is a REST, and these people are using it, but it is just a part of The Web they use. REST is the network architecture: hypermedia encodes the state of resources for hypermedia clients. JSON is an essential part of Single Page Applications, but useless by itself; it can only function in the context of a complete API specification. JSON is normally used in combination with SPA libraries: the whole system is basically RPC with JSON added, or JSON/RPC. All these so-called “REST-ful” APIs are really JSON/RPC.
The situation is even worse than that because JSON-RPC is well specified:
https://www.jsonrpc.org/specification
These so-called "REST-ful" APIs are often ad hoc RPC, underspecified in many respects, and make a twisted mess of HTTP semantics and request/response payloads; and when an edge case or shortcoming is discovered, the API sprouts more wild hairs.
What's even more "fun" is when you look through the project's history and discover that at some point, someone/s on the team fixed the problem by introducing proper JSON-RPC (per spec). Maybe they even got pretty far along with cleaning up most/all of the API. But then they moved on or got pushed aside. Other devs didn't like the JSON-RPC system ("too verbose", "too much effort to refactor/whatever") and started sprouting new "REST-ful" endpoints so they can, you know, get things done faster!
> POST on /queries/enlisted-students-on-joining-date/version/1 { "date": "2023-09-22" } to retrieve all students that joined on a given date.
always ends up in a complete and absolute mess, where every possible query gets it's own random name, different parameters, ending up with duplicates all over the place. Also, while it can be possible to cache these POST requests (responses), it's additional work and more friction compared to REST. I'm not convinced the tradeoff is worth it in this particular case.
All in all, I don't know if the current proposal can be an alternative either but the idea of a "REST-lite" goes in the right direction and it's a great start.
However this is a complete no for me:
> When a requested student is nonexistent, your API can return 200 OK HTTP status code with a { "user": null, "message": "No user exists with the specified ID" } response body.
If nothing is found, I want a 404! :D
- 404 if resource requested by id
- 200 with empty list of results if it was a 'search' type request with params (not referring directly to an id)
In other words, using 404 the way you describe conflates two different things: an invalid user URI (maybe the range of possible user IDs is restricted, or you typed in the URI wrong) and a valid user URI that just doesn't have a user there at the time you made your request. But you probably don't want those two things to be conflated; you want them to be distinguished. That's the article's point.
Now I'm not a web dev, I've played around way back in the CGI era and since then moved on to other things. But my impression was that part of the point of REST was making it fit in a bit better in how the protocol is supposed to behave.
Early on there was a lot of stuff that did nothing but GET for everything. Done that way any layer in the middle can't make any sensible caching decisions, and even the logs are annoying to look at when you can't quickly tell apart what's retrieving data and what's changing it.
So my impression was that part of the point of REST is to shoehorn an API into something that makes sense in the way HTTP is supposed to work, and you're less likely to have something go dramatically go wrong because some cheap ISP is running a transparent proxy and deciding that the GET that's supposed to save something doesn't actually need to happen, because look, this user already made a GET to the same URL 5 seconds ago.
Now of course today everything uses SSL, but early on it wasn't that weird to run into some awful ISP that would do some sort of aggressive caching to save on upstream bandwidth, if not to actually try to recompress stuff in flight.
I do think this throws away some pieces that are really valuable though:
1. URLs for concepts are a good idea
2. Distinguishing between read-only operations (which can be cached) and write operations using GET and POST verbs is useful
Personally I've found myself settling on "REST-ish" JSON APIs:
- Every domain concept has a URL, and a standard consistent JSON representation that's also returned by list endpoints
- GET for read operations, POST for anything that performs a write
- I don't like content negotiation via the Accept header, so instead if I need to support alternative representations I'll do that using file extensions, .json vs .csv for example
GET for read, POST for write. And a url made of a slash-terminated "resource" followed by an "action" verb. user 123 + edit = "/user/123/edit" ; user 123 + default(show) = "/user/123/" ; list of users = "/user/list" ; etc.
When applied to web pages, it means you can have <form action="edit"> and the "action" attribute of the form directly matches the "action" of the backend/router/API. I like that, it makes me feel warm and fuzzy. :-)
I find one good use case for them when a browser can make good use case with a SSE whilst another client just needs a regular _json_ response
Cloudflare doesn't support it for caching (more specific Cloudflare doesn't obey the Vary: cache header).
2. The method to use is implied by the relationship of a link.
I think that ship has sailed. REST in practice just means following HTTP semantics.
If that were true, it would be awesome. Because full HTTP semanics are quite intricate, and cover a lot
- HTTP decision diagram: https://github.com/for-GET/http-decision-diagram (full docs: https://github.com/for-GET/http-decision-diagram/blob/master..., just the PNG: https://camo.githubusercontent.com/c26df6d372790e9f24d7e16d2...)
- Know your HTTP well: https://github.com/for-GET/know-your-http-well
1. Ancient XML/SOAP based APIs Frankenstiened to use JSON
2. Poorly implemented RPCs advertised as "JSON REST API", usually the entire thing either relies on POST requests but sometimes GET is used to make stateful changes.
3. Things that should obviously be RPC split into dozens of anemic JSON endpoints that the caller has to re-construct to get anything useful
Incidently, my company's leadership got inspired by Amazon's API culture and made # of APIs a metric that teams must hit. The result is every single endpoint being deployed as a separate API, and teams blocking access to native vendor APIs so each operation can be republished as a new API. :)
1. First, as other comments here are pointing out, in the industry people use "REST" to mean lots of different things. Some folks just use it to basically mean "An API over HTTP that uses JSON and HTTP methods (GET, POST, etc.)". For clarification I like to refer to that as "REST-lite". The author is talking about what I like to refer to has "true REST", which is strict about entities, verbs, etc. and the format of calls like PATCH.
2. I think the authors general points about why "true REST" is usually a bad idea have been proven out, and like most bad ideas that originated around that time period, they came about because people had a fantasy idea of how we would interact with resources on the web that wasn't born out in reality. I essentially thought his point about "being able to use any API without understanding it first" was spot on. I also think there were other good points about the supposed benefits that nobody really uses, or that aren't that much of a benefit (these days, who really cares about supporting multiple formats of an API like XML in addition to JSON?).
At the end of the day, after decades in software development, I've pretty much come to the conclusion that if your API framework doesn't look like some flavor of RPC, it's bad. This is just fundamentally how most software developers think: "I want to do some operation X, so just let me make a remote procedure call that is named X". This higher-order structure is usually a mistake that's trying to get developers to conform to some ivory tower way of thinking that doesn't work.
> 1) It’s simple, and easy to understand. You can perform the same actions on all resources, so you don’t need to learn the details of a specific API to use it. 2) The API is usable via a browser.
then the part that this article is missing is HTML. REST APIs require HTML (or equivalent), because (via the the browser) the HTML self-describes what actions can be performed on it. If you return `<a href=/profile>My Profile</a>`, then the user knows that clicking on it will take them to their profile; they're interacting with the API without knowing its details.
A lot of people say "ah to hell with it REST has no meaning anymore" but since the premise of the article is to engage with the meaning of REST, well, that's kind of on them.
See the @recursivedoubts essays in another comment on this post too, they're much more in-depth than my comment.
https://faithlife.codes/blog/2023/09/rest-level-zero-dot-fiv...
And this makes most software easier to understand:
"Show me your flowcharts and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowcharts; they'll be obvious." -- Fred Brooks
In the end though, the author isn't really arguing against what we have, expect they want /commands and /queries prefixes.
Given the way some endpoints can be overloaded with disparate behaviors, I don't disagree that sometimes it can be confusing for all the verbal to apply to an endpoint. But POST /students/enroll seems more than fine to me, and trying to wrestle out a new norm, where URLs aren't broken down by resource type but are broken down instead by behavior-class seems absurd & a folly.
> The fundamental issue with REST is that it defines operations that manipulate data structures. It’s about structure and structural changes, not behaviour. It’s imperative, rather than declarative.
If it's about structure, wouldn't that be declarative? Aren't behaviors typically imperative (move this here, drive that there)?
That is, a command on the server that represents a user action, takes input, and returns the updated data that has changed. While many people dislike the whole GraphQL stack (I love it, it solves all the problems the author has with REST), I think we can all agree with the usefullness of the pattern that mutations are built upon.
So many of your users will not make it much past the elevator pitch for your idea. They will feel willfully or even recklessly entitled to the notion that if your tool allows something, the.n it was meant to be used that way.
I don’t have any quick fixes for that. I doubt they exist. There are some very clever things I have seen in the REST domain that are lumped under advanced topics that make a lot of sense but I don’t think I have ever seen in the wild. So if an idea feels important, you have to aim it at the journeyman if you expect it to be used at all, and beginner if you can figure out how without losing them.
Another annoyance is GET endpoints with complex filters like e.g. the index route for a specific resource. It's really annoying to be limited to the query params instead of a request body for that.
Eventually you may get something like a high dimensional protocol used for converting and syncing neural adapters on the fly, enabling "multi-brains" to collaborate opportunistically. But even then there will _still_ be people having this exact same argument.
Anyway, I feel like a lot of people actually moved on to GraphQL years ago. I think I probably need to catch up to that trend still.
The real mistake is graphql. Like microservices, so many companies get hamstrung with this. I’ve said my piece.
REST seems to have different definitions depending who you talk to. Some define it very strictly (as the opening of the article seems to do) and others define it very loosely (as a JSON interface with GET/POST/etc commands). For those who implement REST loosely, the suggested alternatives fit right in.
If there are validation issues with user-provided data in a PATCH, then the API should tell the user what they did wrong and reject the update, plain and simple. How would their solution react to bad data? How would it be different from what any implementation of PATCH does?
> POST on /queries/enlisted-students-on-joining-date/version/1 { "date": "2023-09-22" } to retrieve all students that joined on a given date.
So instead of a browser-accessible GET with idempotency standards, you need a POST and you need to serialize JSON just for a single field?
> POST on /commands/report-student-lastname-change/version/1 { "student-id": "123", "new-lastname": "Kent" } to report a lastname change for a student.
Great naming: I initially thought this would be about a report about last name changes, not a way to add new data.
Should there be separate endpoints for each field of each entity? That might replace “validation” of no changes to fields that cannot be changed, but it does not replace validation of last-name-is-not-empty or national-id-number-is-valid. What if the changes are provided in a form that allows to edit all data? Do I need to send separate commands for each user-edited field? If using the bulk form (POST /commands), what if one of the commands fails — will the previous commands be rolled back, or will the DB end up with partially-edited data?
> When a requested student is nonexistent, your API can return 200 OK HTTP status code with a { "user": null, "message": "No user exists with the specified ID" } response body.
And now you can’t even automatically detect error conditions and have consistent handling for them, at least not with a schema like this.
1. https://khalilstemmler.com/articles/oop-design-principles/co...
I found your problem.
> About being able to use any API without understanding it first
That's a HATEOAS thing, as I understand it. REST just means that you have resources and verbs, and the verbs have ~relatively~ consistent meaning. If I see some GETs and PUTs and PATCHes and DELETEs I can get a pretty good idea of what's going on. It doesn't mean I understand all the details of your application logic.
But in short, articles arguing thst the most popular api format needs to be put to rest can't be anything else but highly opinionated clickbait
Here's the thing, domain specific language specification both in source code and at various API interaction points are a good thing. But they are a thing that takes time to figure out, and they have a tendency to change. REST is still the best starting point and I challenge anyone to say that RPC is "better". We've tried it before it wasn't better. Oh you mean we did it wrong? Well now, that's the real trick isn't it?
The one API I worked with a lot is that of VKontakte. It's as unRESTful as they come. You send requests to URLs like api.vk.com/method/users.get. The HTTP method doesn't matter at all, how you pass parameters doesn't matter at all (can be a combination of query and form-data for all it cares), the version is just `v` parameter, the access token is also just a parameter, and errors come with 200 OK. That is kinda not well thought out. But only kinda.
The one I'm contemplating is somewhere in between the two. The endpoints are still "methods", but the HTTP method does have a meaning: GET is for retrieving something, and POST is for active actions. The access token will have to be passed in Authorization header. The version will still be a `v` parameter. There will not be any IDs in the paths because of how awkward that is. The errors will set the HTTP status code.
Any opinions on this?
Versioned "commands" are an improvement I'd like to see more of (it could even be useful in existing REST-lite APIs - I've used similar concepts to this in some prior work)
And the key point around business logic being misrepresented is definitely worth a ponder.
Notable that a lot of this sounds like a reinvention of DDD (Domain Driven Design) concepts - or at least if the author is applying DDD they make no mention of it. Which I wholeheartedly support..
Seems like two problems being conflated though:
1. Low level interactions - generally between systems. Sometimes I _want_ my API not to hold any high level business logic. It'll do basic verification of individual entity events (changes) to avoid epically stupid mistakes, but otherwise is a dumb resource used by other, more high level controllers.
Often this scenario is basically a thin wrapper over storage mechanisms, or a single abstraction atop a collection of other APIs In this scenario, REST or other "non-business-aligned" options are perfectly fine - if they were any higher level then I'd be constraining the possible actions to what's implemented in a single place.
But by keeping it granular, and aligned explicitly to direct interaction with specific models (in a bounded context) then those changes _should_ be isolated.
2. Intent based APIs which perform high level actions that are aligned to ubiquitous language.
These would be a great fit for the proposed method in the article - and I'd posit that many of us already use some form of this in our APIs, whether through naughty blurring of what "REST" is supposed to mean, using GraphQL, or other similar alternatives.
Generally these are user facing, or at least are at the boundary.
Personally I'd maintain these as a separate thing (or at a separate top level route) which leverages the lower level interfaces to perform more complex, potentially multi-modal activities.
---
*TLDR* what the article proposes seems useful, but (IMO) more as a good reminder to be clear about your APIs' purposes: are they an interface to a bounded context, a model, or an aggregate?
It's definitely easier to link data structures to REST commands--I imagine REST was created with them in mind--but the recommended "commands" can still be thought of as resources. Not every REST verb applies to them, true, but that's not really impactful or relevant. The point is that you can continue structuring your API in typical REST fashion, but with the addition of command-oriented RESTful paths that implement the appropriate verbs for that command.
The author's own suggestion, `POST on /commands/{command-type}/version/{command-version}`, is still effectively REST as far as I'm concerned, with the identifier being a string ('command-type') instead of a number or UUID. The structure of the API hasn't changed, we just broadened our definition of a resource. Purists might disagree there, but from a day-to-day practicality perspective, nothing significant changed in our API development. The article is arguing semantics rather than actually criticizing REST as it's used in practice.
That said, the author does seem to have listed a few things I disagree with:
> So when the server receives a PATCH /schools/{school-ID}/students/234 { "lastname": "Luthor" } request, it needs to understand that a student has requested for their lastname to be changed, and it then needs to decide what to do about it. What if some fields cannot be changed? What if the value of some fields is constrained by the value of some other fields? By allowing an arbitrary PATCH operation, these concepts are hard to model and validate requests against.
I'm not sure what difficulty they're seeing here. If some of the values can't be changed, it's a failed request, return the errors without any updates. The "arbitrary" parts feel like they derive more from their own API implementations than anything practically REST-ful. Or maybe it's in the official REST spec, which, as comments here demonstrate on repeat, holds very little ground.
> This also sucks, because HTTP status codes are about the HTTP protocol, not your business semantics. The 404 Not Found status code indicates that a path doesn’t exist. If you use it to say that a student doesn’t exist, these two get mixed up. If a client invokes the wrong path by mistake, there’s no telling whether it’s due to a protocol error or to a nonexistent student.
If there's no student with the ID 4, then the path `/students/4` doesn't exist. It's not saying that `/students/<id>` doesn't exist, because the path-with-variable only exists as a theoretical construct, not an actual path. So I'm not quite seeing their argument here (unless, again, they're arguing against the semantics of the official recommendation, which I've never seen implemented in a straight fashion anywhere). And if there's user error in invoking the wrong path, then that's hardly on the API design.