I’ve spent some time talking with my teams recently about REST from the perspective of an API, and also how APIs built through the lens of technologies like Swagger go against some of the most important principles of REST. As I’ve reflected more over conversations, there is one even more fundamental thing that tends to get lost in the conversation. And that is that REST doesn’t describe APIs. REST describes the architectural characteristics of an entire system, which includes all of the different components of that system. Trying to make a systems-level claim based on one component, or even one layer, of that system simply doesn’t work.
One of the biggest disservices that we do to REST is to append the term “API”
I’ve now seen the scenario play out in several different companies where a service constructed to be RESTful in the most pure sense of the definition was used by clients in ways that made the system non-RESTful. Worse than that, it made it much harder to articulate the value of REST at a higher level because the “RESTful” API had no discernible differences with regard to properties such as usability and long term stability than its RPC counterparts. In fact, in some cases, properties such as usability were deemed much worse.
There are 2 obvious ways whereby a client can make all the difference between a RESTful and non-RESTful system: URL construction and document structure.
As I’m sure you’ve heard ad-nauseam, avoiding client-constructed URLs in favor of server-supplied links is at the center of REST’s hypermedia constraint. This enables the server to truly own the names and locations for all of its resources, thereby enabling it to evolve independently of its clients. However, this only works if the clients use the links given. And more often than I would like to admit, what I see in real client programs is that they ignore the server links and instead construct URLs from a base URL (many times hard-coded) plus various bits of data in the previous response body. Tools such as Swagger (and even GraphQL) encourage this pattern with their use of URI templates and placeholders.
The much more insidious coupling between a client and server is related to how each comprehends the payload sent in requests and responses. In the ideal world of REST, both client and server would bind to a standard, IANA-registered content type and would not attempt to comprehend anything beyond that. However, this is not the world in which we live, and it’s arguably not a world in which we want to live. Business systems are by their nature, domain specific - with the natural consequence being that even in the same domain each system will have a slightly different dialect from one another. Whether right or wrong, this differentiation is many times even looked on as a competitive advantage. Additionally, business (and the respective data that supports it) can change rapidly in response to a variety of factors. How frequently do you suppose standard media types change? You probably won’t be surprised to learn that it’s measured in years.
So if a client and server can’t rely on binding to a standard content type for mutual understanding of the data being sent back and forth between them, what can they rely on for stability? Well, just like fiat money, nothing :)
Okay, that was admittedly a little dramatic - but the point is that the stability of the system really does rest (no pun intended) on an agreement between server and client authors for how data will be comprehended. Those rules must be documented somewhere and then encoded into both clients and servers. Rules can be documented in markup or in a structured description language, and they can be encoded directly in code, in schema documents, or, in a hybrid case, code generated from schema documents. For all of these strategies, tools and techniques, the case remains that there is a “data contract” (to borrow from my WCF days) that is present in interactions between client and server which is required for system stability. Any change in the data contract necessarily causes a change in the service contract. And in a more complex system of federated services, there are actually lots of little data contracts floating around that a client must bind to. Given that reality, the WSDL and Swagger folks were probably right to fold the document schema into the API procedure description document and present the schema as parameter types (though I still don’t agree with those overall strategies).
Looking at these two aspects of how people build (and want to build) clients really gets to the heart of the matter IMO when it comes to REST. In practice, a RESTful system is predicated on having generic clients - and doing this may be impractical for the type of systems that we are building. There is a reason why there are such a small number of Web browsers. Building a browser is hard. However, it’s a known complexity because of standard content types such as HTML and protocols such as HTTP and DNS; and it’s an endeavor that can be justified based on the number of potential users. For business systems - even large ones - it’s much more difficult to justify a similar endeavor - especially when it would entail the creation (and to be successful, broader industry buy-in) of a standard content type analogous to HTML.
So, perhaps it’s time to be intellectually honest and abandon using the term “REST” when we talk about APIs. This would enable us to stop getting wrapped around the axle of whether they are truly RESTful and simultaneously enable us to not feel compelled to come up with terms like “pragmatic REST” so that we feel better about what we’re doing. Perhaps instead we should focus on building usable Web APIs. Usability puts the focus where it belongs - on our users.