Home » News » 2020 News » How does REST actually help us build APIs?

How does REST actually help us build APIs?

  • by

All right, but apart from the sanitation, the medicine, education, wine, public order, irrigation, roads, the fresh water system and public health, what have the Romans ever done for us?” –Reg (John Cleese), leader of the People’s Front of Judea, in Monty Python’s Life of Brian. (Watch the clip here)

rest

REST is everywhere these days, but let us ask why, and whether those reasons apply to us.

REST essentially defines the architecture of the World Wide Web in its modern form.

The man who coined the term, Dr Roy Fielding, co-author of the HTTP and URI standards (RFCs), described it in his PhD dissertation: “Architectural Styles and the Design of Network-based Software Architectures“, and it is an approach which underlies much of everything we do on the Internet.

Almost all modern APIs tend to describe themselves as “RESTful” (although not all actually qualify under Fielding’s description, but that is another story).

In the Introduction to his dissertation Fielding warns against the phenomenon of “design-by-buzzword“: developers following the latest software fad and only afterwards discovering it is less than ideal for what they were actually attempting to achieve.  So let us evaluate REST (which after all was not originally conceived as an approach to creating APIs) to see if it really brings advantages to the development and adoption of our Application Programming Interfaces (APIs).

The characteristics of REST

Let’s look first at those characteristics Fielding sets out for REST:

  1. Client-Server
  2. Stateless
  3. Cache
  4. Uniform Interface
  5. Layered System
  6. Code-On-Demand

Items 1 and 2 are both simple and obvious for us.  The APIs we generally want to build are going to be, by their very nature, client-server, while it is a characteristic of such services that their interactions are essentially stateless: the client makes a request and the server responds to it… then forgets about it.  Even if credentials are supplied (and in most real-world situations those will be required), those are presented afresh with each and every request1.

Item 6 is not, in general, relevant to clients of APIs, which themselves are specifically developed with any code which is required – the server should not need to serve up specific code for them to function2.

However items 3 and 5 are somewhat problematic for most APIs.  Most often we will actively want to discourage (and indeed defeat) any caching of our returned data, either on the client itself, or in the potentially many elements of Fielding’s “Layered Systems”: gateways, proxies or other intermediate hosts.  We don’t want to see the state of a piece of data (a resource) as it was the last time somebody requested it, we want to see it as it currently is, direct from the horse’s mouth, thank you very much.  I don’t need my bank balance as it was a couple of minutes ago – I want to know what it is now3.

That leaves item 4 – Uniform Interface, and this is really important to us.  We will want our APIs to be completely agnostic as to what platform their clients are running on and with what tools they have been built.  A uniform interface is exactly what we want.  Fielding specifies four constraints that such an interface should satisfy:

  1. Identification of resources
  2. Manipulation of resources through representations
  3. Self descriptive messages
  4. Hypermedia as the engine of application state

The first three of these are satisfied by:

  1. Each resource having its own unique URI (in practice, a URL)
  2. JSON data passed between client and server
  3. The HTTP verbs (in our case GET, POST, PATCH and DELETE)

That final element, “Hypermedia as the engine of application state” (often abbreviated to the somewhat awkward acronym: HATEOAS), is something that is important to the design of an API.  In his blog Fielding states: “A REST API should be entered with no prior knowledge beyond the initial URI“, going on to say: “all application state transitions must be driven by client selection of server-provided choices that are present in the received representations“.

What this implies is that REST APIs should be essentially self documenting and fully discoverable and navigable from their internal representations.

With the SOAP/XML style of web services, we had the medium of WSDL to provide clients with a description of the service, however, there is, as yet, no widely adopted substitute for WSDL in the REST/JSON world4.  The principal of HATEOAS provides an alternative for that.

Making an API “developer friendly”

I am going to explain some of how that works, but first I’d like to do the show before the tell, so consider the following small API (based on our “sacred texts“: the DataFlex WebOrder sample application).

It uses Basic Authentication and valid username/password combinations for it are the familiar “admin/admin”, “John/John” and “guest/guest” (although “guest” has fewer privileges).

By “consider” I mean click on the link below, read what comes back then explore your way around the API by following the links embedded in the data.  As it says there, in the “documentation” element of the results returned from the first call, Firefox works best for this because it formats JSON, and, crucially, the embedded hyperlinks within it, nicely (and clickably). Although any modern browser (other than the antiquated Internet Explorer, which will want you to download the .json file) will actually work, your experience will be much better using Firefox, or something like the Postman API testing tool. (WDREDFU of course stands for “What did REST ever do for us”!)

WebOrder Demonstration API: https://test.unicorninterglobal.com/WDREDFU/api

(Links to download the demo source and the REST library it uses are at the end of the article.)

So… welcome back.  How was that?  Do you think you now have enough information to build a client for that API?  (Frankly, I’m rather hoping that at this point you are cautiously nodding your head and saying “Yes!“)

The point, of course, is that the API itself contains a great deal of information telling a visiting developer how to use it and leading them through the various features and resources it offers.

It starts with the “documentation” element, which tells them the representation format (JSON), which HTTP verbs are used and for what, and finally various parameters which can be passed in the query string of URLs to modify the returned data.  Not least of these is the “meta=on” parameter which when applied to a collection URL returns the metadata for instances within it.

Below that are listed the various top-level “collection” resources: customer, orders, vendors, and so on, with links to those collections.

Following one of those links to a collection will return a JSON object with, by default:

  • An “owner” link (in the top-level case referring to the API root itself, while in dependant collections it will be a link to the instance it depends on)
  • An “href” link to that collection resource (a “self link”)
  • A “count” of the number of instances returned
  • Finally a JSON object, named for the type of instances being returned, containing an array of objects, each with information about a specific instance and an “href” link to it

Following one of those instance “href” links will return a JSON object with, again by default:

  • An “href” link to that instance (another “self link”)
  • A JSON object with members representing the various data items making up that instance
  • A “collection” object with a name property and an “href” link to the “owning” collection (basically, where you probably came from to get there)
  • Possibly a “collections” (plural) object, containing an array of dependent collections which this instance owns (i.e. customers have orders; orders have details, etc.)
  • Possibly a “links” object containing an array of links to other resources which have a relevance to the instance

For example, https://test.unicorninterglobal.com/WDREDFU/api/customers/20 returns the following JSON:

{
    "href": "https://test.unicorninterglobal.com:443/WDREDFU/api/customers/20",
    "customer": {
        "Customer_Number": 20,
        "Name": "Amplified Version",
        "Address": "8303 Dauphin St",
        "City": "Mobile",
        "State": "AL",
        "Zip": "36606",
        "Phone_Number": "251-555-9488",
        "Fax_Number": "251-555-9489",
        "EMail_Address": "amplifiedversion@msn.com",
        "Credit_Limit": 0,
        "Purchases": 0,
        "Balance": 0,
        "Comments": "Music Store.",
        "Status": "Y"
    },
    "collection": {
        "name": "customers",
        "href": "https://test.unicorninterglobal.com:443/WDREDFU/api/customers"
    },
    "collections": [
        {
            "name": "orders",
            "href": "https://test.unicorninterglobal.com:443/WDREDFU/api/customers/20/orders"
        }
    ]
}

Why all that “extra” information?

All of that “extra” information is about providing any developer visiting our API with all the information they need to build a client to exploit its various features, and that is the whole point.

Once a developer has built their client for our API, none of this “extra” stuff matters at all.  In fact, although a developer may simply ignore that “extra” returned information, instead they could actually formulate their request so as to explicitly turn it off.  If, for instance, they only wanted to retrieve a list of customers and their balances, they could request:

https://test.unicorninterglobal.com/WDREDFU/api/customers?fields=customer_number|name|balance&count=off&owner=off&listlinks=off&selflink=off

And get back the JSON:

{
    "customers": [
        {
            "Customer_Number": 1,
            "Name": "Access Miles",
            "Balance": 10198
        },
        {
            "Customer_Number": 2,
            "Name": "American Products, Inc.",
            "Balance": 14287
        },
        {
            "Customer_Number": 3,
            "Name": "Ortega Emergency Care Center",
            "Balance": 7632
        },

        ... etc.

}

Or if they only wanted to retrieve the actual instance data, they could request:

https://test.unicorninterglobal.com/WDREDFU/api/customers/20?selflink=off&collection=off&dependents=off

And only get back the JSON:

{
    "customer": {
        "Customer_Number": 20,
        "Name": "Amplified Version",
        "Address": "8303 Dauphin St",
        "City": "Mobile",
        "State": "AL",
        "Zip": "36606",
        "Phone_Number": "251-555-9488",
        "Fax_Number": "251-555-9489",
        "EMail_Address": "amplifiedversion@msn.com",
        "Credit_Limit": 0,
        "Purchases": 0,
        "Balance": 0,
        "Comments": "Music Store.",
        "Status": "Y"
    }
}

So why return such apparently redundant information as, for instance, the “self link” href by default?  Surely the client will already know that, having just used it to access the resource?

Well the logic goes like this…

When creating a new resource in a collection (via a POST to it), the only thing you actually need to return to the client is the server-generated key to that new resource, in order that the client can use that to reference it in future.  However better than just the key is the actual URL required to access that resource (which will contain that key).

With update (PATCH) or deletion (DELETE) operations, you could technically return nothing at all with simply an HTTP status of “204 – No Content”.

When retrieving a resource (GET) however, you will obviously want to return the (representation of) the resource itself.

Now consider the task of a developer building a client for your API.  If you return different things from different operations – create, read, update and delete – that developer will require to create different data structures and different handling code for each, on every resource their software needs to access.  However if you always return exactly the same thing, only a single data structure will be required while much of the handling code can be common to all operations.

Again, if the developer actually wants to get back the minimum response, they can specify turning off all the “extra” stuff, including even returning the instance details.  So for example, updating that resource (using PATCH) with the following call:

http://test.unicorninterglobal.com/WDREDFU/api/customers/20?selfLink=off&collection=off&dependents=off&returnInstance=off

Passing, say, the JSON:

{
    "Address": "1211 University Boulevard",
    "City": "Jackson",
    "Zip": "36904",
}

Which will return nothing at all in the response body, with an HTTP status of “204 – No Content”.

The bottom line

The bottom line here is that it is all very well creating an API, however that is of no real use if no third parties develop applications to use it.  The adoption of your API is the crucial thing to making it a success.  The key to that is making it as simple as possible for developers to do that – make their life easy and you greatly increase the chances that they will successfully build client software to access it.

The alternative would be to provide copious documentation for every operation of your API, but nobody really likes writing documentation, in part at least because developers rarely want to read it – they want to get on with writing their software, as quickly and easily as possible.  At the same time, documentation, like comments in code, often becomes out of date as the underlying software evolves – when was the last time you updated your documentation to reflect the changes you made to the software it purports to being a guide to?

So to return to the question in the title, what does REST actually do for us, API-wise?

If we follow its principles, it offers us a way to structure our APIs so as to make them easily accessible and understandable to our potential audience – third party developers – with only the provision of that “initial URI” Fielding refers to, encouraging and facilitating them in building clients to access them.

Downloads

You can download the current REST library as a zip file from here and the AppSrc directory of the sample used in the article from here.

Notes:

1. There is however a whole discussion about how best to do that, which I will not get into here.

2. The purpose of code-on-demand is to allow a general-purpose tool (typically a web browser) to be provided with page or situation specific functionality – typically as JavaScript or a Java applet.

3. To be fair, there is a discussion to be had about the trade-offs between reducing latency through caching and the scalability that brings, which could be achieved through the complex process of cache-invalidation, but for most of us who are not Google or Microsoft or Amazon, requiring to operate at “Internet Scale”, we generally don’t want it.

4. That said, there are ongoing efforts in this direction – see, for instance, Swagger and the Open API specification.