REST API - REST Architectural Constraints

REST stands for Representational State Transfer, a term coined by Roy Fielding in 2000. It is an architecture style for designing loosely coupled applications over HTTP, that is often used in the development of web services. REST does not enforce any rule regarding how it should be implemented at a lower level, it just put high-level design guidelines and leave you to think of your own implementation.

 
REST Architectural Constraints are design rules that are applied to establish the distinct characteristics of the REST architectural style.

If you follow all constraints designed by the REST architectural style your system is considered RESTful. 

REST Architectural Constraints

REST defines 6 architectural constraints that make any web service – a true RESTful API.
  • Client-Server
  • Stateless
  • Cacheable
  • Uniform Interface
  • Layered System
  • Code On Demand (Optional) 

Client-Server

This constraint keeps the client and server loosely coupled. In this case, the client does not need to know the implementation details in the server, and the server is not worried about how the data is used by the client. However, a common interface is maintained between the client and server to ease communication. This constraint is based on the principle of Separation of concerns.
Figure: Client-Server
Applying separation of concerns: 
  • Separates user interface concerns from data storage concerns.
  • Improves portability of interface across multiple platforms.
  • Improves scalability by simplifying server components.
  • Allows the components to evolve independently.

Stateless

There should be no need for the service to keep user sessions. In other words, the constraint says that the server should not remember the state of the application. As a consequence, the client should send all information necessary for execution along with each request, because the server cannot reuse information from previous requests as it didn’t memorize them. All info needed is in the message.
 Figure: Stateless
By applying statelessness constraint:
  • Session state is kept entirely on the client.
  • Visibility is improved since a monitoring system does not have to look beyond a single request.
  • Reliability is improved due to easier recoverability from partial failures.
  • Scalability is improved due to not having to allocate resources for storing state
  • The server does not have to manage resource usage across requests. 

Cacheable

This constraint has to support a caching system. The network infrastructure should support a cache at different levels. Caching can avoid repeated round trips between the client and the server for retrieving the same resource.
 Figure: Cacheable
By adding optional non-shared caching:
  • Data within a response to a request is implicitly or explicitly labeled as cacheable or non-cacheable.
  • If a response is cacheable, then a client cache is given the right to reuse that response data for later, equivalent requests.
  • Improves efficiency, scalability, and user-perceived performance.
  • Tradeoff: cacheable constraint reduces Reliability.

Uniform Interface

This constraint indicates a generic interface to manage all the interactions between the client and server in a unified way, which simplifies and decouples the architecture. This constraint indicates that each resource exposed for use by the client must have a unique address and should be accessible through a generic interface. The client can act on the resources by using a generic set of methods.
Figure: Uniform Interface
By applying uniform interface constraint:
The overall system architecture is simplified and the visibility of interactions is improved. Implementations are decoupled from the services they provide and encourage independent evolvability. 

Trade-off: Degrades efficiency since information is transferred in a standardized form rather than one which is specific to the application's needs. Further, a uniform interface has four sub-constraints
  • Identification of resources
  • Manipulation of resources through representations
  • Self-descriptive messages
  • Hypermedia as the engine of application state (HATEOAS)

Identification of resources

Each resource must have a specific and cohesive URI to be made available. REST APIs are designed around resources, which are any kind of object, data, or service that can be accessed by the client.
A resource has an identifier, which is a URI that uniquely identifies that resource. 
For example, the URI for a particular customer order might be:
http://adventure-works.com/orders/1
Resource representation – This is how the resource will return to the client. This representation can be in HTML, XML, JSON, TXT, and more. Clients interact with a service by exchanging representations of resources. Many web APIs use JSON as the exchange format. For example, a GET request to the URI listed above might return this response body:
{"orderId":1,"orderValue":99.90,"productId":1,"quantity":1}

Self-descriptive Messages

Each message includes enough information to describe how to process the message. Beyond what we have seen so far, the passage of meta information is needed (metadata) in the request and response. Some of this information are: HTTP response code, Host, Content-Type etc. Taking as an example the same URI as we have just seen:
GET /#!/user/ramesh HTTP/1.1
User-Agent: Chrome/37.0.2062.94
Accept: application/json
Host: exampledomain.com

Hypermedia as the Engine of Application State (HATEOAS)

REST APIs are driven by hypermedia links that are contained in the representation. For example, the following shows a JSON representation of an order. It contains links to get or update the customer associated with the order.
Just one example:
{
    "orderID":3,
    "productID":2,
    "quantity":4,
    "orderValue":16.60,
    "links": [
        {"rel":"product","href":"http://adventure-works.com/customers/3", "action":"GET" },
        {"rel":"product","href":"http://adventure-works.com/customers/3", "action":"PUT" } 
    ]
} 

Layered System

The server can have multiple layers for implementation. This layered architecture helps to improve scalability by enabling load balancing. It also improves the performance by providing shared caches at different levels.
Figure: Layered System
By applying a layered system constraint:

  • Similar to the client-server constraint this constraint improves simplicity by separating concerns.
  • Can be used to encapsulate legacy services or protect new services from legacy clients.
  • Intermediaries can be used to improve system scalability by enabling load balancing
  • Placing shared caches at the boundaries of the organizational domain can result in significant benefits. Can also enforce security policies e.g. firewall.
  • Intermediaries can actively transform message content since messages are self-descriptive and their semantics are visible to the intermediaries Tradeoff: Adds overhead and latency and reduce user-perceived performance.

Code on Demand

This constraint is optional. This constraint indicates that the functionality of the client applications can be extended at runtime by allowing a code download from the server and executing the code. Some examples are the applets and the JavaScript code that get transferred and executed at the client-side at runtime.
Figure: Code on demand

By applying Code on demand constraint:
  • Simplifies clients, hence promote the reduced coupling of features.
  • Improves scalability by virtue of the server off-loading work onto the clients.
  • Trade-off: Reduces visibility generated by the code itself, which is hard for an intermediary to interpret.

Conclusion

In this post, we have seen the 6 important REST Architectural Constraints.

Now we understand that these 6 architectural constraints which make any web service – a truly RESTful API.

These constraints of the REST architectural style affect the following REST architectural properties.

Comments