The REST goals
Now that we've covered the main REST principles, let's dive deeper into what can be achieved when they are followed:
- Separation of the representation and the resource
- Visibility
- Reliability
- Scalability
- Performance
Separation of the representation and the resource
A resource is just a set of information, and as defined by Principle 4, it can have multiple representations; however, its state is atomic. It is up to the caller to specify the desired media type with the accept header in the HTTP request, and then it is up to the server application to handle the representation accordingly and return the appropriate content type of the resource and a relevant HTTP status code:
HTTP 200 OK
in the case of successHTTP 400 Bad Request
if an unsupported content type is requested or for any other invalid requestHTTP 500 Internal Server Error
when something unexpected happens during the request processing
For instance, let's assume that at server side, we have balance resources stored in an XML format. We can have an API that allows a consumer to request the resource in various formats, such as application/json
, application/zip
, application/octet-stream
, and so on.
It would be up to the API itself to load the requested resource, transform it into the requested type (for example, JSON or XML), and either use ZIP to compress it or directly flush it to the HTTP response output.
The caller can make use of the Accept
HTTP header to specify the expected media type of the response data. So, if we want to request our balance data inserted in the previous section in XML format, the following request should be executed:
GET /data/balance/22082014 HTTP/1.1 Host: my-computer-hostname Accept: text/xml HTTP/1.1 200 OK Content-Type: text/xml Content-Length: 140 <?xml version="1.0" encoding="utf-8"?> <balance date="22082014"> <Item>Sample item</Item> <price currency="EUR">100</price> </balance>
To request the same balance in JSON format, the Accept
header needs to be set to application/json
:
GET /data/balance/22082014 HTTP/1.1 Host: my-computer-hostname Accept: application/json HTTP/1.1 200 OK Content-Type: application/json Content-Length: 120 { "balance": { "date": "22082014", "Item": "Sample item", "price": { "-currency": "EUR", "#text": "100" } } }
Visibility
REST is designed to be visible and simple. Visibility of the service means that every aspect of it should self-descriptive and follow the natural HTTP language according to principles 3, 4, and 5.
Visibility in the context of the outer world would mean that monitoring applications would be interested only in the HTTP communication between the REST service and the caller. Since the requests and responses are stateless and atomic, nothing more is needed to flow the behavior of the application and to understand whether anything has gone wrong.
Tip
Remember that caching reduces the visibility of your restful applications and in general should be avoided, unless needed for serving resources subject to large amounts of callers. In such cases, caching may be an option, after carefully evaluating the possible consequences of serving obsolete data.
Reliability
Before talking about reliability, we need to define which HTTP methods are safe and which are idempotent in the REST context. So, let's first define what safe and idempotent methods are:
- An HTTP method is considered to be safe provided that when requested, it does not modify or cause any side effects on the state of the resource
- An HTTP method is considered to be idempotent if its response is always the same, no matter how many times it is requested
The following table lists shows you which HTTP method is safe and which is idempotent:
HTTP Method |
Safe |
Idempotent |
|
Yes |
Yes |
|
No |
No |
|
No |
Yes |
|
No |
Yes |
Scalability and performance
So far, I have often stressed the importance of having stateless implementation and stateless behavior for a RESTful web application. The World Wide Web (WWW) is an enormous universe, containing a huge amount of data and a lot of users eager to get that data. The evolution of the WWW has brought the requirement that applications should scale easily as their load increases. Scaling applications that have a state is hardly possible, especially when zero or close-to-zero downtime is needed.
That's why being stateless is crucial for any application that needs to scale. In the best-case scenario, scaling your application would require you to put another piece of hardware for a load balancer. There would be no need for the different nodes to sync between each other, as they should not care about the state at all. Scalability is all about serving all your clients in an acceptable amount of time. Its main idea is to keep your application running and to prevent Denial of Service (DoS) caused by a huge amount of incoming requests.
Scalability should not be confused with performance of an application. Performance is measured by the time needed for a single request to be processed, not by the total number of requests that the application can handle. The asynchronous non-blocking architecture and event-driven design of Node.js make it a logical choice for implementing an application that scales and performs well.