What are RESTful Web Services?
To put it mildly, the World Wide Web was an unexpected success.
What had started out as a convenient way for research labs to connect with each other suddenly exploded in size. Jakob Nielsen estimated that between 1991 and 1997 the number of web sites grew with a staggering 850% per year each year!
This incredible growth worried some of the early web pioneers, because they knew that the underlying software was never designed with such massive amount of users in mind.
So they set out to define the web standards more clearly, and enhance them so that the web would continue to flourish in this new reality where it was suddenly the world’s most popular network.
One of these web pioneers was Roy Fielding, who set out to look at what made the internet software so successful in the first place and where it was lacking, and in his fascinating PhD dissertation he formalized his findings into six constraints, which he collectively called REpresentional State Transfer (REST).
Fielding’s observation was that if your architecture satisfies these six constraints then it will exhibit a number of desirable properties (like scalability, decoupling, simplicity), which are absolutely essential in an Internet-sized system.
His idea was that the constraints should be used as a checklist to evaluate new potential web standards, so that poor design could be spotted early, and way before it was suddenly deployed to millions of web servers.
He successfully used the constraints to evaluate new web standards, such as HTTP 1.1 (where he was one of the principal authors) and URI (where he was also one of the authors). These standards have both stood the test of time, despite the immense pressure of being essential protocols on the web and used by billions of people each day.
So a natural question to ask is that if following these REST constraints lead to such great systems, why only used them for browsers and web sites? Why not also create web services that conform to them, so we can enjoy the desirable properties that they lead to?
This thinking led to the idea of RESTful Web Services, which are basically web services that satisfy the REST constraints, and are therefore well-suited for Internet-scale systems.
So what are these 6 REST constraints?
1. Client-Server
The first constraint is that the system must be made up of clients and servers.
Servers have resources that clients want to use. For example, a server has a list of stock prices (i.e. a resource) and the client would like to display these prices in some nice graphs.
There is a clear separation of concerns between the two. The server takes care of the back-end stuff (data storage, business rules, etc.) and the client handles the front-end stuff (user interfaces).
The separation means that there can be many different types of clients (web portals, mobile apps, BPM engines, etc.) that access the same server, and each of these can evolve independently of the other clients and the server (assuming that the interface between the clients and server is stable).
The separation also seriously reduces the complexity of the server, as it doesn’t need to deal with UI stuff, which improves scalability.
This is probably the least controversial constraint of REST as client-server is so ubiquitous today that we almost forget that there are other styles to consider (like event-based protocols).
It important to note that while HTTP is almost always used when people develop RESTful Web Services, there is no constraint that forces us to use it. We could use FTP as the underlying protocol, if we really wanted. Even though intellectual curiosity is probably the only good reason for trying that.
2. Stateless
To further simplify interactions between clients and servers, the second constraint is that the communication between them must be stateless.
This means that all information about the client’s session is kept on the client, and the server knows nothing of it (so no cookies, session variables, or other naughty stuff!) The consequence is that each request must contain all information necessary to perform the request (i.e. it cannot rely on any context information).
The stateless constraint simplifies the server as it no longer needs to keep track of client sessions, resources between requests, and it does wonders for scalability because the server can quickly free resources after requests have been finished.
It also makes the system easier to reason about as you can easily see all the input data for a request and what output data it resulted in. You no longer need to lookup session variables and other stuff that makes the system harder to understand.
In addition, it will also be easier for the client to recover from failures, as the session context on the server has not suddenly gotten corrupted or out of sync with the client. Roy Fielding even goes as far as writing in an old newsgroup post that reliance on server-side sessions is one of the primary reasons behind failed web applications and on top of that it also ruins scalability.
So far nothing too controversial in the constraints. Many RPC implementations could probably satisfy both the Client-Server and Stateless constraints.
3. Cache
The last constraint on the client-server communication is that responses from servers must be marked as cacheable or non-cacheable.
An effective cache can reduce the number of client-server interactions, which contributes positively to the performance of the system. At least, from a user’s point of view.
Protocols, like SOAP, that only uses HTTP as a convenient way to get through firewalls (by using POST for all requests) miss out on the improved performance from HTTP caching, which reduces their performance (and also slightly undermines the basic purpose of a firewall.)
4. Uniform Interface
What really separate REST from other architectural styles is the Uniform Interface enforced by the fourth constraint.
We don’t usually think about it, but it’s pretty amazing that you can use the same Internet browser to read the news, and to do your online banking. Despite these being fundamentally different applications. You don’t even need an extension to the browser to do any of this!
We can do this because the Uniform Interface decouples the interface from the implementation, which makes interactions so simple that it’s easy for somebody familiar with the style to understand it, even automatically (like Googlebot).
The Uniform Interface constraint is made up of 4 sub-constraints:
4.1. Identification of Resources
The REST style is centered around resources. This is unlike SOAP and other RPC styles that are modeled around procedures (or methods).
So what is a resource? A resource is basically anything that can be named. From static picture to a feed with real-time stock prices.
But in enterprise software the resources are usually the entities from the business domain (i.e. customers, orders, products, etc.) On an implementation level, it is often the database tables (with business logic on top) that are exposed as resources. But you can also model a business process or workflow as resource.
Each resource in a RESTful design must be uniquely identifiable via an URI (Uniform Resource Identifier) and the identifier must be stable even when the underlying resource is updated (i.e. “Cool URIs don’t change”).
This means that each resource you want to expose through a RESTful web service must have its own URI. Normally, you would use the first URI below to access a collection of resources (i.e. several customers) and the second URI to access a specific resource inside that collection (i.e. a specific customer):
1) https://api.example.com/customers 2) https://api.example.com/customers/932612
Some well-known APIs that claim to be RESTful fail this sub-constraint. For example, Twitter’s REST APIs uses RPC-like URIs like statuses/destroy/:id and it’s the same with Flickr.
The problem is that they break the Uniform Interface requirement, which adds unnecessary complexity to their APIs.
4.2 Manipulation of Resources through Representations
The second sub-constraint in the Uniform Interface is that resources are manipulated through representations.
This means that the client does not interact directly with the server’s resource. For example, we don’t allow the client to run SQL statements against our database tables.
Instead, the server exposes a representation of the resource’s state. It can sound complicated, but it’s not.
It just means that we show the resource’s data (i.e. state) in a neutral format. This is similar to how the data for a web page can be stored in a database, but is always send to the browser in HTML.
The most common format for RESTful web services is JSON, which is used in the body of the HTTP requests and responses:
{ "id": 12, "firstname": "Han", "lastname":"Solo" }
When a client wants to update the resource, it gets a representation of that resource from the server, updates the representation with the new data, send the updated representation to the server, and ask the server to update its resource so it corresponds with the new representation.
The benefit is that you avoid a strong coupling between the client and server (like with RMI in Java), so you can change the underlying implementation without affecting the clients. It also makes it easier for clients as they don’t need to understand the underlying technology used by each server that they interact with.
4.3 Self-Descriptive Messages
The third constraint in the Uniform Interface is that each message (i.e. request/response) must include enough information for the receiver to understand it in isolation.
Each message must have a media type (for instance, application/json or application/xml) that tells the receiver how the message should be parsed.
HTTP is not formally required for RESTful web services, but if you use the HTTP methods you should follow their formal meaning, so the user won’t rely on out of band information to understand them (i.e. don’t use POST to retrieve data, or GET to save data).
So for the Customer URIs, which we defined earlier, we can expose the following methods for the client to use:
Task | Method | Path |
---|---|---|
Create a new customer | POST | /customers |
Delete an existing customer | DELETE | /customers/{id} |
Get a specific customer | GET | /customers/{id} |
Search for customers | GET | /customers |
Update an existing customer | PUT | /customers/{id} |
The benefit is that the four HTTP methods are clearly defined, so an API user who knows HTTP, but doesn’t know our system can quickly guess what the service is doing by only looking at the HTTP method and URI path (i.e. if you hide the first column, a person who knows HTTP can guess what it says based on the two new columns).
Another cool thing about self-descriptive message is that (similar to statelessness) you can understand and reason about the message in isolation. You don’t need some out-of-band information to decipher it, which again simplifies things.
4.4 Hypermedia as the Engine of Application State
The fourth and final sub-constraint in the Uniform Interface is called Hypermedia as the Engine of Application State (HATEOAS). It sounds a bit overwhelming, but in reality it’s a simple concept.
A web page is an instance of application state, hypermedia is text with hyperlinks. The hypermedia drives (i.e. engine) the application state. In other words, we click on links to move to new pages (i.e. application states).
So when you are surfing the web, you are using hypermedia as the engine of application state!
So it basically means that we should use links (i.e. hypermedia) to navigate through the application. The opposite would be to take an Customer ID from one service call, and then use it as an input parameter to another service call.
It should work like a good web site where you just enter the URI and then you just follow the links that are provided on the web pages. You don’t need to know more than the initial URI.
For example, inside a customer representation there could be a links section with links to the customer’s orders:
{ "id":12, "firstname":"Han", "lastname":"Solo", "_links": { "self": { "href":"https://api.example.com/customers/12" }, "orders": { "href":"https://api.example.com/customers/12/orders" } } }
The service can also provide the links in the Link HTTP header, and W3C is working on a standard definition for the relation types, so we can use standardized meanings which further helps the user.
An enormous benefit is that the API user doesn’t need to look in the API documentation to see how to find the customer’s orders, so he or she can easily explore while developing without having to refer to out-of-band API documentation.
It also means that the API user doesn’t need to hardcode (and manually construct) the URIs that he or she wants to call. It might sound like a trivial thing, but Craig McClanahan (co-designer of The Sun Cloud API) wrote in an informative blog post that in his experience 90% of client defects were caused by badly constructed URIs.
Roy Fielding didn’t write that much about the hypermedia sub-constraint in his PhD dissertation (due to lack of time), but he later wrote a blog post where he clarified some of the details.
5. Layered System
The fifth constraint is another constraint on top of the Uniform Interface, which says that the client should only know the immediate layer it is communicating with, and not be aware of any layers behind it.
This means that the client doesn’t know if it’s talking with an intermediate, or the actual server. So if we place a proxy or load balancer between the client and server, it wouldn’t affect their communications and we wouldn’t need to update the client or server code.
It also means that we can add security as a layer on top of the web services, and then clearly separate business logic from security logic.
6. Code-On-Demand (optional)
The sixth and final constraint is the only optional constraint in the REST style.
Code-On-Demand means that a server can extend the functionality of a client on runtime, by sending code to it that it should execute (like Java Applets, or JavaScript).
I have not heard of any RESTful web services that actually send code from the server to the client (after deployment) and gets it executed on the client, but could be a powerful way to beef up the client.
A really nice feature of the simplicity that is enforced by these six constraints (especially, uniform interface and stateless interactions) is that the client code becomes really easy to write.
Most modern web framework can figure out what to do, if we follow the conventions above and they can take care of most of the boilerplate code for us.
For example, in the new Oracle JET toolkit, we simply need the JavaScript below to create a customer (and it would be just as easy in AngularJS):
// Only code needed to configure the RESTful Web Service var Customer = oj.Model.extend({ urlRoot: "http://api.example.com/customers", idAttribute: "id" }); // Create a new customer representation var customer = new Customer(); customer.attributes.firstName = "Han"; customer.attributes.lastName = "Solo"; // Ask the server to save it customer.save().then(function() { console.log("Saved!"); });
And it’s just as easy to call the other HTTP methods.
So the front-end engineer just need to add a few more lines to add an HTML form where the user can enter the values, and voila we have a basic web app!
And if we use one of the many gorgeous UI frameworks (like Twitter’s Bootstrap or Google’s Materialize), we can quickly develop something really nice looking in a really short time.
That’s it for now. Thank you for reading and take good care until next time!