Question

I'm designing a HATEOAS API for internal data at my company, but have been having troubles with the discovery of links. Consider the following set of steps for someone to retrieve information about a specific employee in this system:

  1. User sends GET to http://coredata/ to get all available resources, returns a number of links including one tagged as rel = "http://coredata/rels/employees"
  2. User follows HREF on the rel from the first request, performing a GET at (for example) http://coredata/employees

The data returned from this last call is my conundrum and a situation where I've heard mixed suggestions. Here are some of them:

  1. That GET will return all employees (with perhaps truncated data), and the client would be responsible for picking the one it wants from that list.
  2. That GET would return a number of URI templated links describing how to query / get one employee / get all employees. Something like:

    "_links": {
        "http://coredata/rels/employees#RetrieveOne": {
            "href": "http://coredata/employees/{id}"
        },
        "http://coredata/rels/employees#Query": {
            "href": "http://coredata/employees{?login,firstName,lastName}"
        },
        "http://coredata/rels/employees#All": {
            "href": "http://coredata/employees/all"
        }
    }
    

I'm a little stuck here with what remains closest to HATEOAS. For option 1, I really do not want to make my clients retrieve all employees every time for the sake of navigation, but I can see how using URI templating in example two introduces some out-of-band knowledge.

My other thought was to use the RetrieveOne, Query, and All operations as my cool URLs, but that seems to violate the concept that you should be able to navigate to the resources you want from one base URI.

Has anyone else managed to come up with a good way to handle this? Navigation is dead simple once you've retrieved one resource or a set of resources, but it seems very difficult to use for discovery.

Was it helpful?

Solution

Option 2 is not too bad as you're using RFC 6570 to characterize the URI patterns; while HATEOAS is usually stated in terms of not having clients synthesize URIs, if a server is prepared to make guarantees on the URI template and to tell it to clients explicitly in a standard format, it's acceptable. (I would be tempted to have the “list all employees” URL be without the all suffix, so as to distinguish it from the employee with that ID; the client should not — in principle — know what an employee ID looks like.)

In fact, the main problem is actually that clients have to understand what those tag URIs mean; there's just no real way to guess that “http://coredata/rels/employees#All” means “list all employees”. That's where you get into embedding knowledge in clients, semantic labeling, etc. and HATEOAS doesn't really address those things.

OTHER TIPS

TL;DR: Use OPTIONS method to return programmatically consumable documentation and always implement pagination.

We create a number of internal REST services at my work. We have standardized on the use of the OPTIONS method to return the metadata of a resource. The metadata we return acts a parsable documentation of that resource. It indicates url templates, various options such as PAGE, PAGESIZE and the different methods that the resource supports. We also return rel links so top level resource discovery can occur with the use of OPTIONS without pulling and actual data.

We also implement pagination specifically to prevent issues around returning large amounts of data unnecessarily.

My HATEOAS API returns HTML as well as HAL+JSON, as you are using, and they both use the same URIs, so my JSON responses simply return what a human web user would see (minus all the pretty colours). e.g.

GET /


{"_links": {
    "http://coredata/companies": { "href": "/companies?page=1" }
    ...
}}



GET /companies?page=1


{"_links": {
    "next": { "href": "?page=2" }
    ...
}}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top