Skip to content

Designing RESTful Clients

Adarsh Kumar Maurya edited this page Dec 10, 2018 · 1 revision

Overview

After the last module you hopefully now have an approach that you can try out for designing and building RESTful services. However, as you've also seen throughout this course, REST is all about describing the complete network interactions between clients and servers. Since the system made up of just one component would be a pretty boring system. So in this module we're going to talk about some strategies for designing and building RESTful clients. Now, the format for this module may feel a bit looser than in the previous modules. This is because in my opinion the process for designing RESTful clients is a little bit more open then it is for services. And this is because there is much greater diversity in the number and types of clients than there are for services. So this module will be structured into basically three sections. First we'll go through some of the challenges that can make RESTful clients more difficult to design and build then clients in some other architectural styles. We'll also revisit the benefits of REST to see why overcoming those challenges might be worth it. Next, I'll present an approach that you can follow in designing a RESTful client and we'll walk through an example where we design part of a command line client for the bug tracking service that we designed in the previous module. Finally we'll look at the key principal to keep in mind when designing a RESTful client, remembering the contract between client and server. We'll review the constraints that make up the uniform interface and I'll give some practical examples for how you can use some of the related data elements in designing your clients.

RESTful Clients Can Be Harder

And while I don't think it ends up being prohibitively so, I think there is a reality to building RESTful clients can be harder than building service oriented applications in other architectural styles. Particularly RPC. I believe the primary reason for this is based on the definition of the contract. Remember that in the RPC world, the contract is generally a service description document. This document outlines all of the service end points, the operations that they make available, the data types used to represent the arguments and return values, the possible exceptions that can be raised by an operation, even different policies that can be applied to services. The key thing is that all of this information is generally specified in one place as a part of what could be thought of as a self-contained application protocol definition. In REST on the other hand, the contract between client and server is the uniform interface itself. this contract is both less restrictive and more restrictive in some of the aspects that I mentioned above. For example, there are no services and operations in a RESTful system, only resources and standard control data elements such as http verbs. Also in REST there are no data types, no arguments, no return values. There are simply media types which again, are a mere sequence of bytes from the viewpoint of the uniform interface. And a resource can support multiple media types which can be negotiated between the client and the server. Finally, in REST, hypermedia driven workflow enable the relationships between clients and servers to be much more dynamic in the sense that components can be moved around. Resources can be added etc all without breaking clients. All of this dynamism and looser coupling generally comes at a cost for folks wanting to design and build RESTful clients. And this is what I really mean when I say that designing RESTful clients can be harder. The cost is that compared with what's available for RPC architectures, the availability of tools to perform tasks such as generating proxy objects is not as simple to do for REST. In REST the network is not something to be abstracted away. And as I hope you're starting to see, this is a good thing. At the end of the day though, whether or not you should consider a RESTful design, depends on your particular application needs. Just as a reminder, here is some of the key properties or benefits of REST. You remember heterogeny, which is seamless inner operability between connectors regardless of language or platform. With scalability we scope complexity to a uniform interface and a single communication layer. With evolve ability clients and servers can evolve without making each other or the entire system unstable. Visibility means that the state of the system can be determined by examining messages. Having this visibility means that we can develop rich compensation strategies that can be built against the uniform interface and the self describing messages, making our system more reliable. For efficiency, local and intermediate caches can take load off of origin servers, enabling them to handle more clients. Similarly those same local and intermediate caches can improve the speed of getting a response improving performance. Finally, layering architectural elements enables the overall system to grow in complexity without impacting an individual layer, making each layer independently manageable. At a high level you could say that REST is optimized for run time experience including things like stability, ease of modification etc. And optimizing in this way can have a negative impact on design time or development time experiences. So one of the first things for you to determine is whether or not your application should be optimized for run time or for development time.

Client Design Process

So even though we're talking about client design in this module the key design metaphor is the same as what we discussed in the previous module on service design. And that is, what would your design look like if you had no computers? Just paper and some way to organize paper. In the previous module we looked at elements of this metaphor such as how information should be arranged on the paper and how we move papers between different buckets as a part of a workflow. RESTful client design is more analogous to the people in our paper based workflow illustration, who take papers from one container, do some work on them and put them into another container. It's the space in between, if you will. Let's revisit our bug workflow from the last module. Remember that our application simulates a typical bug tracking workflow that uses a dry erase board and sticky notes. In the last module we spent a large part of time answering questions such as what does the sticky note look like and what does the board look like? As a reminder you can see both illustrated here. as you can see, our sticky note has a couple pieces of data for things such as title, description and the initials of the person to whom the bug is assigned. Additionally we've defined some iconography to both the sticky note and the bug board. This will provide visual cues to the human in this module, the client, to know how to move the bug through the workflow on the board. And thereby making the actual workflow explicit rather than what usually happens which is to have the workflow as implicit knowledge which is shared by the team. if I'm designing a RESTful client the first obvious question that I need to know so that I can start designing, is a little bit more fundamental. Where's the board? We're going to design a client for the bug tracking service that we designed in the previous module and as a part of that design I'm going to propose a workflow that you can see here. The entry point for client design is to take a read through the documentation of the media type or the media types being used to support the service. This will give you a baseline understanding of the media type syntax as well as an understanding of any additional semantics that the media type supports. For example, does it support hypermedia and if so what hypermedia controls does it provide. Once you've reviewed the media type documentation it's equally important that you understand how the service's business domain has been mapped onto the type. Particularly if the chosen media type is domain general or domain agnostic. Documentation can be very helpful here but I typically find sample documents to be more helpful for this part of the workflow. I'll usually start creating my client design based on the sample documents and use the domain mapping specification for support or for validation. Once you have a good expectation about what representations will look like the next step is to make a request to the service entry point resource and get a real representation. As you can see here, I'm using the curl tool for this. Next, take a look at the response representation and see how well it lines up to what you were expecting after having looked at the media type specification and sample documents. Assuming that it looks consistent, if it's possible go ahead and write client functionality that relies on this resource. Next, based on your inspection of the returned representation take a look at the links that it contains and based on the client feature that you're trying to implement, follow that link and repeat the entire process until you've completed your client application. The key point here is that because of constraints like statelessness and the uniform interface, your client learns arrest API through the process of discovery rather than needing to understand the entire API at a single point in time.

Example: Bug Tracking Requirement

So let's take a sample requirement from our bug tracking application. We want to be able to discover bugs in various states. Some examples here are backlog, working, QA and done. So let's walk through the process and iteratively design our client. In the previous module you may remember that I decided to use html as my media type because of its support for hypermedia controls as well as its ability to enable testing the API using a browser. But another benefit to html is that I already know it. and as such my first step was honestly not to go to the html specification so that I could understand the media type. Though I did refer to it when looking at attributes like class and REL. However, this may not always be the case. As an example let's say that I wanted to use a Jason based media type that also had support for hypermedia. In this case I would likely choose the Hal Jason format. Because I'm not intimately familiar with Hal the first place to start would be at the media type specification shown here. if you want to learn more about Hal make sure to look at the references associated with this module. By looking at the specification I can determine the general structure for organizing data as well as how links and link semantics are to be formatted. Because html is a domain agnostic media type I also need to look at something to tell me how my business domain, bugs in this case, is mapped onto an html representation. There are two primary ways that I can acquire this information. From a mapping specification and from sample documents. Like I mentioned a minute ago I generally use the sample documents as a starting point and refer back to the specification as necessary. Either to validate my understanding of the sample document or to clarify when the sample documents seem ambiguous. However, where you decide to start is really more a matter of what works best for you. And more importantly what is available to you. From example some service authors may publish only one type of reference or another. We now have an idea of what a representation should look like. So let's issue a request to get a representation for the services entry point resource. Again, I'm using curl for this task because it's very lightweight. Just in case you're curious the H parameter tells curl that I want to specify an http header. In this case I'm specifying some representation Meta data, the except header, so that I can ensure that the server knows that I want html in the return representation. Issuing the curl request gives me the following html representation. As you can see, this is completely valid html and I've mapped my bug tracking domain onto it using attributes such as ID, class and REL. The document is divided into three sections, bugs, forms and links which is expected based on the sample document and the mapping specification. Also you can see that even in this entry point representation there are quite a few things I can do. the most notable being that I can add a new bug. However, remember the requirements that we're trying to design at the moment. We want to enable a user to discover bugs in various states. As such, the only section that we're interested in right now is the links block at the bottom. In this block you can see that there are quite a few anchor elements with link relationships that identify the various states that a bug could be in. So let's start there.

Bug Client Code

Now I know I said at the very beginning of the course that we wouldn't be looking at any code and for the most part that's true. But this approach to design involves getting your hands dirty as quickly as possible so you will have to forgive me because we will be looking at some code here. Our client in this module is a simple console application written in C Sharp. However, the code is simple enough that I expect you can translate it into your language of choice without any issue. Based on our requirement of enabling a user to discover bugs in various states and based on what I can see from our entry point representation, there are three basic pieces of functionality that we need to write for our client. We need to be able to display links to all of the various bug state collection resources. We also need to enable the user to activate one of these choices and get the collection of bugs in that state. Finally, we need to be able to display a list of bugs to the user. The code for displaying the list of bugs looks very much like the code shown here for displaying the list of links. So I'm not going to show it here in this module though the entire sample application is available online. See the references section for more on that. However, as you can see here I've parsed my html response into an xml document and displaying the list of links involves issuing a standard X path query based on a known ID attribute value. The key thing here is that the client doesn't need to understand the entire document by way of some kind of serialization mechanism. It cares about just one thing. Being able to write out a list of navigation links. After showing the user a list of possible links that he or she can use to discover bugs in various states, the next thing that we need to do is enable the user to actually choose a list of bugs to see and then go fetch that list. just like we did in the previous example we find the appropriate element by way of our X path query. In this case we're relying on two bits of domain mapping information that was specified out of band by our domain mapping specification. The state block identifier which shows up as the ID of the div and the link relationship type on the anchor tag. Assuming that the user has entered a valid link relationship identifier in the link command value, this code then simply extracts the value of the H rep attribute and D references the URL to get a new representation. Now, one thing I want to call attention to here, you may be saying to yourself, wow, that X path looks really ugly. Is REST really this much of a step backwards? One of the realizations that I came to, particularly as I was implementing this sample, is that while media types like html are great in this sense, that the media type specification defines a rich set of hypermedia controls. Those controls are not all that helpful if you don't have a user agent or a library that can actually work intelligently with the media type. For example, when surfing my API the browser gives a complete html document object model or dom to work with. However, in my console application I don't have this kind of object model. And so I'm having to work with a semantically rich media type format as if it were a much less meaningful xml document. I bring this up because as you're evaluation what media types you want to support in your RESTful design, it's important to consider what types of clients you think you will need to support and pick a media type based, in part at least, on what kind of library support exists for that media type. So we examine the relevant specifications and sample documents, issued a request to the entry point resource, received a representation and even wrote client code to display a list of links representing collections of bugs in various states and letting the user follow one of those links. The next step is to iterate through that loop again by following one of those links ourselves. As you can see here I'm simply picking one of those links and using Curl again to follow it. I can then continue to iterate on my client in this way until I've implemented all of the desired client functionality.

Remember the Contract

Now while I hope that the workflow I've presented works for you in designing your RESTful clients, the reality is that people are different and so different approaches work better for some than others. In fact, I'll be shocked if I still use the same workflow in a year or two since, just like you I'm always growing and improving the way that I do things. More important than the design approach though, is this. However you approach designing RESTful clients you need to remember the contract and again, in REST this is the uniform interface. And it consists of four things. Identification of resources, manipulation through representations, self descriptive messages and hypermedia as the engine of application state.

Clients and Resources

While the identification of resources is generally more of a server design activity, a client should be aware of the different types of resources that are available so that you as the client designer can decide what you're interested in when inspecting a representation. More importantly, knowing the available set of resources will help you make better sense of link relationships when you encounter them. past that, you don't really want your client to care all that much about the details of resources, and particularly resource identifiers, as this can lead to undue coupling between clients and servers. While your client shouldn't need to know that much about resources themselves the resources Meta data that servers may provide can prove very helpful in optimizing the communication between client and server. For example. Consider the following http exchange. This exchange takes advantage of some control data along with a very helpful piece of resources Meta data. The entity tag or e tag. This Meta data is passed to the client in a response header and can be used but the client to make what's known as a conditional request. We talked briefly about conditional requests back in module three. Now, most people identify conditional requests with conditional get requests as a way to optimize for caching scenarios. However, as you can see here, they are also helpful for ensuring that put of post operations don't end up overriding resources that shouldn't be overridden. In this exchange two clients are trying to update a resource. I omitted showing client two getting the resource for brevity sake. However, the important thing to note is that when client two updates the resource the value of the e tag changes. when client one then tries to update the same resource, it issues the update request with the if match header containing the e tag value that represents that last state it had for resource one. This conditional header is equivalent to saying update resource one if the current state of the resource matches the value that I'm presenting here. Because the state of resource one has changed as a result of client two's update, the update cannot progress and the server returns a failure status code. Because the client is aware of control data such as status codes, and we'll talk more on that later, it can intelligently react by getting the most recent representation of resource one. This response will contain an updated e tag value of two. The client can then ask the user to do some conflict resolution if appropriate and then issue the same conditional update request with the new e tag value. Assuming that the state of the resource has not been changed by another client during the conflict resolution process, the update will succeed and a new e tag value will be included in the resource Meta data of that response.

Clients and Representations

As we've talked about in past modules a representation is how a client actually interacts with a resource. And multiple representations can be supported by a server. From the client design perspective the key principal is to not make any assumptions about what representations a server will return for a resource. Instead you should rely on representation Meta data both in requesting a specific representation from a server and in processing the specific representation returned. Also unlike in RPC representations are not simply serialized data types. As such, they may, over time, have items added to them that are not relevant to your client. However, just because a bit of data in a representation isn't relevant to your client it certainly shouldn't break your client either. Finally, as we talked about in the last module, if you need to change your system in a way that changes your representation format, you can take advantage of https content negotiation ability to perform your versioning. Let's do a quick demonstration of versioning a media type using content negotiation. Here you can see a bug's representation that should look pretty familiar. The thing that's different is that we've changed the value of the except header from text html to a custom media type. Also note that the media type name include a version number. On the response side my client can look at the content type header to see what is contained in the response body. It can use this information to process the response body using the appropriate processor or it can take some other action if it doesn't have a valid processor for the value specified in the content type header. Again, the key thing is that the client makes a decision about how to process the content based on the representation Metadata and not the representation content. Now let's assume that as a part of evolving my service I need to rename some link relationships. This is not a very desirable thing to have to do but it happens from time to time. So for the purpose of this illustration that's the data we're going to change. This change doesn't fundamentally change the meaning of the mapping between a resource and its entities so we don't need to mint a new resource. However it does change the meaning of elements within a representation. And so we need to version the media type. In the example here we include a version moniker in the actual media type name. Therefore all the server needs to do is to start supporting the version two format. At their own pace clients can start evolving to support the new format by requesting it in the accept header. Clients that evolve more slowly can continue to request the version one format. Now, particularly with regard to http know that you don't have to create a custom media type definition in order to use content negotiation to support representation versioning. Http provides several different options here from media type parameters to custom headers. You can perform content negotiation on any of these. The key is that you use content negotiation to version your representation formats.

Clients and Self-Describing Messages

We've seen tons of examples throughout this course where we've based decisions, both for the server and the client on Metadata. Whether it was resource Metadata, representation Metadata, or control data. this is really the essence of self descriptive messages. The more your clients understand all of these various available message information bits, the more intelligently they can deal with many of the challenges outlined in module two. Such as shifting network topology or unreliable network or server infrastructure. In fact, let's look at a quick example of that latter challenge. Sometimes strange intermittent errors happen on the server or anywhere in between the server and client. Typically when you build a client using an RPC framework any kind of transient failure causes an exception to be thrown in the client and many times this leaves the client and the user in a state of being stuck. Http actually has a status code that can be used to indicate this type of temporary failure. As you can see here the client attempts to get a resource but can't because of some temporary outage. As a result the server returns a 503 status code with some control data instructing the client to retry the request after 30 seconds. If the client is designed to understand these semantics it can simply wait for the specified time and hopefully achieve success on the following request. The user may have to wait a little longer but a wait is always better than a failure.

Clients and Hypermedia

Like I've mentioned in the last couple of modules, I'm a big believer in the hypermedia constraint. Your client should be designed firstly as hypermedia consumers. As I've been demonstrating throughout the module, your clients should only need to know about an entry point resource. And that resource should provide the link or links that enables your client to get to all of the different capabilities of your service. as an optimization your client may hang onto specific URLs and action known as bookmarking so that it can jump to different parts of your application. However, know that these can possibly shift so your client should always be prepared to deal with a 404 status code and go back to the entry point if necessary. Secondly the client should treat URLs in a representation's hypermedia controls as nothing more than opaque strings. The client should not try and interpret any kind of meaning from them, rather, the client should look for the meaning of a link from its associated link relationship value. For example, remember earlier in this module my anchor tag had an H ref value of bugs, slash, backlog and a REL value of backlog. My client should only care about the REL value. In fact, should I want to, I should be able to change the H rep value to a guid and still not break my client. And just so you don't think that this is me being dogmatic, I wanted to show you this quote and this is directly from Roy Fielding the guy who created REST. Now, and remember that back in module three we talked about all of the different types of links. Hypermedia controls can include everything from the standard anchor tag which falls into the outgoing link category here to templated, to item potent and non item potent. Link relations are specified either as a part of the hypermedia control's definition as is the case with the html anchor tags REL attribute or you can apply link relationships using a more neutral mechanism such as htlm class attribute. In my example this is how I chose to identify link relationships for html form elements since those elements don't have a REL attribute. So take a look at this mark up. Here we're using html's form hypermedia control, to update the state of a resource. We're specifying two different link relationship types, new and backlog. The html specification gives us this capability by declaring the multiple values can be held by a class attribute so long as they are separated by a space. The action attribute represents the target of the hypermedia control or the address that will be de-referenced by activating the control. the method attribute tells the client what http method to use when translating this state transfer block into a new request. Additionally you can specify what media type you want to use for the representation that will be sent to the server. However, the html specification defines form URL encoded as the default encoding type. And that's what I would have chosen anyway so there was no need to explicitly call it out. After the form definition you can see that the representation includes a few different input elements. A client can simply examine these elements, fill in the appropriate values and then translate the entire form into a new http request and representation in order to update the state of the resource, identified by the value of the form action attribute. And as you can see, when activated the client turned the form into a new http post request containing a representation of the media type or murl encoded. Additionally the input elements were converted into a series of name value pairs to form the form URL encoded payload.

Summary

So to recap. In this module we've talked through some of the challenges to building RESTful clients. We then looked at one approach to designing RESTful clients that enables the design to evolve rather than having to address every challenge at once. We then stepped back and looked at various aspects of the key principal to keep in mind when designing clients. The contract or in our case the uniform interface. At this point we've covered all of the background and design aspects of building RESTful systems. Both servers and clients. In the next module we're going to look a little more to the future and see how REST can help in building applications for the Cloud.

Clone this wiki locally