Finding a restful resource when using HATEOAS?
Asked Answered
B

4

6

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?

Boaster answered 25/1, 2015 at 7:42 Comment(6)
Where is the need to fetch by an id coming from? What is the actual use case?Dyanne
Server side app displays a link to a client side app to edit a specific order, based on orderid. user clicks link, client side app opens upp and need to fetch the order.. has to be a pretty common scenario( ?)Boaster
Is the client app a dynamic one? Meaning, you are not doing page reloads, but state transitions using XHR?Dyanne
Yes, SPA type app, but opened from the server app UI, the server does not know about the rest api in that case, it gets the order ID from DB.. (legacy app beeing migrated to SPA)Boaster
Then Stefan's answer is likely what you need. Process and RFC 6570 URI Template to 'inject the state' from you legacy server into the REST API.Dyanne
Once you have the legacy state transitioned into the REST API state, you can follow links from there for any further state transitions.Dyanne
B
6

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.

Bias answered 25/1, 2015 at 19:47 Comment(4)
Doesn't the rel links come dangerously close to adding new verbs to the stack? my impression was that links was intended to link to other resources ad not as a facade for new verbs to the self link?Boaster
rel defines a relationship between resources. If the verb is interesting it will be represented by its own resource, so the resource referenced by the cancel action might be an OrderCancellation. This would be the reified version of the operation cancelling an order.Bias
Link relation types can be used to document semantics of how URI templates can be populated. Yes, that creates coupling, however, it is coupling that can evolve, because new link relations can be used in the future. REST does not completely remove coupling, it simply focuses coupling in media types and link relation types, so that the coupling can be more easily identified and managed.Speed
Looking back on this response, the discovery part sounds like GraphQL to me. The API discovery entry point is the introspect of GraphQL schema.Rocker
H
5

You do it using exactly the same concept you’d use in any web app: By sending the client a recipe for building the next request. In HTML, you’d use an HTML form to do that. If you’re using JSON, your format would need to have the same concept, e.g. using a URI template, or even a form, i.e. a list of key-value pairs, possibly with some of the values already filled in.

As an example, a previous request might return something like this:

{ "order": {
    "link": {
        "template": "https://your-api.com/orders/{id}",
        "method": "GET",
        "type": "application/json"
    }
  }
}

Of course your code would still be dependent on some information – in this case, the fact that the id is called "id" in the template. But by adding this indirection, it doesn’t know about the actual URI. Also note that I’ve added method and type parameters as an example; whether you choose to make that configurable depends on the format you use. For a more elaborate example, see (or use) the excellent collection+json format.

Horick answered 25/1, 2015 at 9:6 Comment(1)
So say we have an edit form, then the workflow would be something like: -> request root /api -> response of your example -> build URL -> request order. is that correctly understood?Boaster
C
5

While you can supply an URI Template, I always consider that a bit of a smell when I have to do it. Sometimes it's necessary, but if a major part of my API ends up using that approach, I wouldn't call it HATEOAS or a level 3 RESTful API.

How to model the scenario is much dependent on the context, and that's probably the reason why Pete asks about the use case.

Often, you can model a RESTful API in the same way as you'd model a human-usable web site. For example, you could have a resource that lists all of a user's orders, sorted e.g. with the most recent orders first:

{
    "orders": [
        {
            "date": "2015-01-25",
            "total": 1234,
            "links": [
                {
                    "rel": "order",
                    "href": "https://follow.the.link"
                }
            ]
        },
        {
            "date": "2015-01-22",
            "total": 1337,
            "links": [
                {
                    "rel": "order",
                    "href": "https://follow.this.other.link"
                }
            ]
        }
    ]
}

I client can look for the order it needs in this list, and then, once it's identified the interesting order, can follow the corresponding link that has an "order" relationship type.

As a general resource, the RESTful Web Services Cookbook is a indispensable for answering these sorts of questions.

Caveator answered 25/1, 2015 at 13:5 Comment(3)
Exactly the answer in my head. The need to transition from legacy system to REST API seems to require the compromise of the URI Template.Dyanne
Would you consider the HTML FORM used for searching a RESTful smell?Speed
@DarrelMiller I wouldn't use an HTML form, because that'd typically force clients to change 'representation set'; clients would typically be using either JSON or XML, so why force them to suddenly use HTML?Caveator
A
1

Let us take your situation. They way i would look at it. Your search always returns a interaction (i.e URL). Then the client can always start navigating from there to get to the data.

The key is to not to return the value but only return the representation.

We are using a platform for this implementation. It has a unique interaction based language. The platform as it is completely HATEOAS compliant, returns URL, when a search is send to it.

In your example if we send a booking id. It returns all order url which has this booking id . The client then can navigate thru these urls.

I hope this helps with your query.

Assai answered 26/2, 2015 at 14:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.