I just released Module 10 in the Master Class of my "REST With Spring" Course:

>> THE "REST WITH SPRING" CLASSES

Table of Contents

1. Overview

This article will focus on Discoverability of the REST API, HATEOAS and practical scenarios driven by tests.

2. Why make the API Discoverable

Discoverability of an API is a topic that doesn’t get enough well deserved attention, and as a consequence very few APIs get it right. It is also something that, if done correctly, can make the API not only RESTful and usable but also elegant.

To understand discoverability you need to understand that constraint that is Hypermedia As The Engine Of Application State (HATEOAS); this constraint of a REST API is about full discoverability of actions/transitions on a Resource from Hypermedia (Hypertext really), as the only driver of application state.

If interaction is to be driven by the API through the conversation itself, concretely via Hypertext, then there can be no documentation, as that would coerce the client to make assumptions that are in fact outside of the context of the API.

In conclusion, the server should be descriptive enough to instruct the client how to use the API via Hypertext only, which, in the case of a HTTP conversation, may be the Link header.

3. Discoverability Scenarios (Driven by tests)

So what does it mean for a REST service to be discoverable? Throughout this section, we will test individual traits of discoverability using Junit, rest-assured and Hamcrest. Since the REST Service has been previously secured, each test first need to authenticate before consuming the API.

3.1. Discover the valid HTTP methods

When a REST Service is consumed with an invalid HTTP method, the response should be a 405 METHOD NOT ALLOWED; in addition, it should also help the client discover the valid HTTP methods that are allowed for that particular Resource, using the Allow HTTP Header in the response:

@Test
public void
 whenInvalidPOSTIsSentToValidURIOfResource_thenAllowHeaderListsTheAllowedActions(){
   // Given
   String uriOfExistingResource = restTemplate.createResource();

   // When
   Response res = givenAuth().post( uriOfExistingResource );

   // Then
   String allowHeader = res.getHeader( HttpHeaders.ALLOW );
   assertThat( allowHeader, AnyOf.<String> anyOf(
    containsString("GET"), containsString("PUT"), containsString("DELETE") ) );
}

3.2. Discover the URI of newly created Resource

The operation of creating a new Resource should always include the URI of the newly created resource in the response, using the Location HTTP Header. If the client does a GET on that URI, the resource should be available:

@Test
public void whenResourceIsCreated_thenUriOfTheNewlyCreatedResourceIsDiscoverable() {
    // When
    Foo newResource = new Foo(randomAlphabetic(6));
    Response createResp = givenAuth().contentType("application/json")
      .body(unpersistedResource).post(getFooURL());
    String uriOfNewResource= createResp.getHeader(HttpHeaders.LOCATION);

    // Then
    Response response = givenAuth().header(HttpHeaders.ACCEPT, "application/json")
      .get(uriOfNewResource);

    Foo resourceFromServer = response.body().as(Foo.class);
    assertThat(newResource, equalTo(resourceFromServer));
}

The test follows a simple scenario: a new Foo resource is created and the HTTP response is used to discover the URI where the Resource is now accessible. The tests then goes one step further and does a GET on that URI to retrieve the resource and compares it to the original, to make sure that it has been correctly persisted.

3.3. Discover the URI to GET All Resources of that type

When we GET any particular Foo resource, we should be able to discover what we can do next: we can list all the available Foo resources. Thus, the operation of retrieving an resource should always include in its response the URI where to get all the resources of that type, again making use of the Link header:

@Test
public void whenResourceIsRetrieved_thenUriToGetAllResourcesIsDiscoverable() {
    // Given
    String uriOfExistingResource = createAsUri();

    // When
    Response getResponse = givenAuth().get(uriOfExistingResource);

    // Then
    String uriToAllResources = HTTPLinkHeaderUtil
      .extractURIByRel(getResponse.getHeader("Link"), "collection");

    Response getAllResponse = givenAuth().get(uriToAllResources);
    assertThat(getAllResponse.getStatusCode(), is(200));
}

Note that the full low level code for extractURIByRel – responsible for extracting the URIs by rel relation is shown here.

This test covers the thorny subject of Link Relations in REST: the URI to retrieve all resources uses the rel=”collection” semantics.

This type of link relation has not yet been standardized, but is already in use by several microformats and proposed for standardization. Usage of non-standard link relations opens up the discussion about microformats and richer semantics in RESTful web services.

4. Other potential discoverable URIs and microformats

Other URIs could potentially be discovered via the Link header, but there is only so much the existing types of link relations allow without moving to a richer semantic markup such as defining custom link relations, the Atom Publishing Protocol or microformats, which will be the topic of another article.

For example the client should be able to discover the URI to create new Resources when doing a GET on a specific Resource; unfortunately there is no link relation to model create semantics. Luckily it is standard practice that the URI for creation is the same as the URI to GET all resources of that type, with the only difference being the POST HTTP method. Forms can also be used to achieve this.

5. Conclusion

We have seen how a REST API is fully discoverable from the root and with no prior knowledge – meaning the client is able to navigate it by doing a GET on the root. Moving forward, all state changes are driven by the client using the available and discoverable transitions that the REST API provides in representations (hence Representational State Transfer).

This article covered the some of the traits of discoverability in the context of a REST web service, discussing HTTP method discovery, the relation between create and get, discovery of the URI to get all resources, etc.

The implementation of all these examples and code snippets can be found in my github project – this is an Eclipse based project, so it should be easy to import and run as it is.

The Master Class of my "REST With Spring" Course is finally out:

>> CHECK OUT THE CLASSES

  • This is an interesting point you raise – and I don’t think there’s any perfect solution to this problem. I have however implemented a few approaches and have seen others work as well.
    In your case, first – a few notes:
    – when you create a user, is should be a POST on /users or similar – not sure that /username is the best URI for this
    – supporting OPTIONS requests should let you know that you can do POST on the /users URI – to figure out that it supports creation of a new user
    One option is to go beyond the standard rel types and do something like: rel=”create” and rel=”edit” to signal to the user that this is a create URI.
    So a developer, consuming the service, coudl do an OPTIONS on /users, see that it supports POST, and then send a POST with a new user and get back a 201 Created with the Location header set to the new URI of the user.

    Hope this helps.

    Thanks.

    • Pierre Primot

      Hello,

      and what if only a subset of the attributes of the user can be modified depending on the authorization of the connected user ?
      I thought hateoas was about returning the state of the resource and the allowed “links”, actions (avoiding the need of the OPTIONS roundtrip).
      I see the hateoas response as a html page that contains the data, the possible actions (hyperlinks,forms), but without formatting elements.
      Am I wrong ?

      • You’re definitely not wrong. The OPTIONS request is not part of that conversation – it’s just a helper. The main conversation should indeed be including the available actions in the response – and that’s what the tests in the article show. Now – if the format in which that’s done is via the Link header or something else (well established hypermedia types or a custom hypermedia type of your own) – that’s another question, but the high level conversation has the same goals.
        One quick side-note about OPTIONS is that there’s no reason why you cannot tune the response to that request according to the credentials the user provides – so basically an admin-type user sending an OPTIONS request can get a larger set of available actions than a non-admin type user.
        Cheers,
        Eugen.

  • Stephane

    In 3.3 what is the difference between a Foo instance and a Foo resource ?

    • No difference – the language is just somewhat ambiguous – I’ll clarify the wording there – thanks.

  • vvarma

    nice article. you should also consider adding a bit about Interceptors.. found them to be a great tool for exception handling

  • Stephane

    Hi Eugen, I cannot see any pagination handling on this page and the table of contents link was implying.

  • Stephane

    I now found some pagination example at http://www.baeldung.com/2012/01/18/rest-pagination-in-spring/ I shall have a look. Thanks !

    • Glad you found it Stephane – do let me know if you run into anything else. Cheers,
      Eugen.

  • Rubén Pahíno Verdugo

    Hi Eugen,

    I’ve just dealt with your guide these days, so first of all thank you for all these contents. I have two different doubts:

    1) “Luckily it is standard practice that the URI for creation is the same as the URI to GET all resources of that type, with the only difference being the POST HTTP method”

    That’s true but, is it a good practice to have a CreateRequest object for sending as the request body when trying to create a new object? Or it’s better to send the same object as the GET response?. I’ve found myself in a very common example where I have to create an Event that happens in a certain Place. In that case I have to send a request that has almost all the event information (skipping the id) but also all the place information (also skipping the id). I think the answer is obvious but I’m afraid of being completely wrong.

    2) The Link and Location approach seems correct for me when I send a simple object but what happens if I send a List of events with their places? Now I think it would be correct to give the information needed to access all those resources (each event and each location). I’ve read in several places to add metadata fields to the body in that cases where this discoverability information doen’t apply to the whole response object. Can you confirm if that is correct and if it’s a good practice to send all this “extra” information.

    Cheers,

    Rubén

    • Hey Ruben – I’m glad you’re enjoying the guides.
      1. That depends on your domain and also on how strictly you want to follow the REST constraints.
      Here’s one way to think about it (but it’s worth going much deeper into this than these few notes here).
      When you design your public API and your Resources, PUT has more clearly defined semantics, while POST affords you more options. You can send Resources via POST, or you can send Commands (the C in CQRS). My personal preference is to try to use the same Resource if I can, and only if Commands are a more natural fit with that domain, do I consider switching to Commands.
      However, even if you go for Resources only, you don’t have to necessarily use the exact same, full representation. For example, you could retrieve the full event (with the embedded place info) here: /events/1
      And you could create the Event here: /place/12/events/ – so that you’re creating inside a place and you don’t have to include the Place information.
      2. The Link and Location approach is a very simple way to do things. I’d recommend going with Spring-HATEOAS for a real-world scenario.

      Hope that helps. Cheers,
      Eugen.

      • Rubén Pahíno Verdugo

        Thank you for your answer

        1) I prefer to go on with resources given my domain and the design. In this case is it ok to send the objects in the create request (with POST) with the empty id?

        2) I’m checking Spring-HATEOAS and it seems to be what I was looking for so again thank you 🙂