Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding a restful resource when using HATEOAS?

When reading about HATEOAS/Hypermedia constraint, one thing that I often see is that a resource should have a self/href of some kind. The argument for this is that the client should not need to know how to construct the URL for that specific resource, e.g. for updating the resource.

e.g.

{
     //instead of "id":"123"
     "href":"/api/v1/orders/123",

     order state...
}

I love that idea.

But how does that concept fit with fetching data? Let's say I need to fetch an order with a specific order id, how would the client deal with that? I still need to know how to construct the URL for the resource in that case, right?

How is the client supposed to know where and how to look for a resource? It has to know about the API URL's in some way or another?

like image 535
Roger Johansson Avatar asked Jan 25 '15 07:01

Roger Johansson


1 Answers

A well-designed HATEOAS API will have clear entry points, and the rest of the application behaviour will be discoverable from there.

Since the question is about HATEOAS specifically I would say using a URI template puts too much responsibility onto the client. Instead you should provide an explicit URL for each valid action on the resource given the current application state.

This isn't just a stylistic point. If the server provides a template then the client developer has to write code to populate the template, which creates a coupling between them. You can't change the server-side URL structure now without breaking the contract with its clients. With HATEOAS you associate a URL with each action allowed on a resource, and the client just cares about the action. The URL is effectively an opaque handle: "Choose your own adventure", as Ian Robinson says.

HATEOAS is about using hypermedia—media containing links to other media—to enable a client to navigate around an application with no other knowledge than the last response it received. That means each response should provide the client with ready-to-use URLs representing all valid actions on the current resource.

Remember, the thing-you-get-over-the-wire is only a representation of a resource (REST stands for REpresentational State Transfer). There can be different representations of the same resource based on your current context, say your current set of permissions, and the current application state. Different representations can legitimately offer different next actions.

Using your example, the owner of an order might see this:

{
  "id": "/api/v1/orders/123", // reference to the current resource
  "rel": {
    "cancel": {
      "url": "/api/v1/orders/cancel?order_id=123",
      "method": "POST",
      // Metadata about what the cancel operation returns...
    },
    "list_orders": {
      "url": "/api/v1/orders",
      "method": "GET",
      // Metadata about what the list_orders operation returns...
    },
    // ...
    // Other operations available to the owner
  },
  // ...
  // Order state
}

Here I'm defining a map that uses the key as the operation name, or relation in HATEOAS terminology, although I could equally have a list of maps with a key called "rel" and values of "cancel" and "list_orders" respectively.

Another role, say the shipping coordinator, may not see the cancel operation because they den't have permission to cancel an order.

like image 89
Daniel Terhorst-North Avatar answered Oct 03 '22 16:10

Daniel Terhorst-North