I was in the process of creating a REST API for a client. The first discovery I made was that myself, and a lot of others, have a wrong definition of REST. The loosely, and wrong, definition goes something like this
WRONG: A set of endpoints that you can call with http requests with a belonging http verb such as GET, POST, PUT and DELETE. The endpoints return XML or JSON.
I knew that my definition was lacking precision. The definition was working and was pragmatic in the context of my previous projects. That does not mean that it will work in the future. Also an in-precise definition keeps you from communicating effectively with the team members and the community at large. So lets correct it and make a precise definition:
REST stands for Representational state transfer and is a software architectural style.
So REST is actually a software architecture style. Let us for a moment forget all about HTTP and the Web and go straight to the source of where this architectural style was first mentioned so we can get some unfiltered information about it. REST was first defined in Fieldings paper in chapter 5. I will not restate everything that is in the paper. But I do find it worth while elaborating a little on some of the aspects. For example Fielding mentions in his paper his views on architectural design, and their drives.
Fielding describes 2 approaches to defining an architectural design:
Often when we make software for clients we have the focus on the requirements. The requirements drive the design, for better or worse. And it is the client of course who drive the requirements. She wants to have some search functionality through documents, the actions done by the administrator should be logged to file, when a test user changes something there should be sent out an email. You get the idea. What is noteworthy here is that the requirements can go against the constraints of the platform you are using.
Another approach to architectural design is constraints driven. Think of a forest with big rocks, large trees and lakes. If you where to build an old cabin you would shape it so it fits in the environment. You could of course bring a large part of dynamite and get to work, but for the sake of arguments lets pretend you don´t have that. In this example the environment forms the cabin to a large degree. REST is defined using a constraints driven approach. The forest in this example is the internett of REST.
So what are the constraints in REST?
Constraints in REST
He mentions several constraints about this architectural style. They can be grouped into the following categories:
- Layered system
- Code on demand
- Uniform interface
A uniform interface separates the clients from the server. Clients are not concerned about data storage and the server is not concerned about client state. You can switch out the client or server without the other one knowing.
The server does not hold any state. The client should hold any session state and potentially send it with each request to the server. It is however possible for the server to transfer the state and store it in a database. The client would then send a token with each request identifying for the server how to retrieve back the state.
The client sends requests to the server with state transitions. And the client is in a state transition when it has one or more outstanding requests. The representation of each application state contains links which the client can use to navigate to a new state. This last part is particularly important because this is where most miss out when building a “REST Api”.
The representation of each application state has links which the client can use to navigate to a new state.
We will later see how a hypermedia format can help the client find these links in the state representation.
The responses in a REST architecture must be cacheable. It means that they must state themselves as cacheable, or not, to prevent the client from using stale data.
The client can not tell whether it is directly or indirectly connected to the server. And the potentially intermediates are invisible to the client. They can improve scalability or enforce security policies without the client knowing.
Code on demand
The last constraint is the uniform interface. It consists of several parts. It is fundamental to the design of restful services, and is where most so called “restfull APIs” fail. Let us start with the identification of resources.
Identification of resources
So the identification of resources are identified in each of the requests. The resource, however, is conceptually different from the representation returned to the client. Take the web for example. What is returned in a request /api/user/5xb could be an xml file describing the user with id 5xb. The user 5xb could be something completely different than an xml file on the server itself. It is also easy to fall for the temptation to think that resources equals entities in our system. That is not true.
Manipulation of resources through these representations
This is an important point where a lot of so called REST api fails. It states that when the client holds a representation of a resource it has enough information to be able to modify or delete the resource. You get a person resource represented in JSON or XML for example. In that representation you should be able to see links or other information which you can use to make further requests to modify or delete the resource.
This constraint tells us that the representation should hold enough information so that the client knows how to process it, for example which parser to use. In the internett world this could be the internett media type.
Hypermedia as the engine of application state(HATEOS)
This is yet another point where the so called REST api often fails. They return a resource represented in plain JSON. The client can parse JSON but have no idea what the different parts are and can therefore not process it further. The hypermedia as the engine of application state means that the client make state transitions only through actions that are dynamically identified with hypermedia by the server. The only exception is the starting point of the application. So you point your client to a fixed entry point, make a request and by the response you make potentially further requests depending what you want to do. This is vastly different from the ordinary so called REST api´s where you have a detailed document describing the different endpoints with their required input and what you get as output. For an example of HATEOS see Dr. Einar Høst presentation on NDC:
Take a look at the hypermedia format Siron. Here we see that once we have a client that can parse and display the siron hypermedia format we can navigate and make state transitions regardless of changes in the API. So the entire problem with versioning API´s falls. Just as you do not need to update your browser to read the new version of Amazon.com you would not need to update your clients as long as the API return a agreed upon hypermedia format.
Richardson´s Maturity Model
To call an architecture RESTfull is a zero sum game. Either the architecture is RESTfull or is it not. However, many refer to richardson’s maturity model when defining their API’s.
My clients API
So now that we have set the stage by defining REST, let ut get back to where we started. My clients API. It is easy to fall into a state of paralixzation. I would read more about REST and get to know more and more about what I do not know. But first decision was: should I create a REST API or not. It might seem like the most obvious question but I think that most of of the people I read about in blogs and articles about REST settle for a middle state in the Richards Maturity model. I find it a bit contradictory to have a rest maturity model. Either the api is REST or not.
The clients of the APIs did not implement a parser of other that JSON as hypermedia format. They where not interested in representing state transitions to the end user that the HATEOS constraint would require. So we went halfway and used HAL as the hypermedia format to standardize the navigation throughout the API. We used API versioning in the URL, e.g., /api/v2/customers/5xb/tickets/.
Further we used Swashbuckle to autogenerate the documentation.