Question

I'm working a REST-ful API in which resources which are fairly interrelated. Resources reference each other, and these references may be created or deleted. I'm a little uncertain how to support associating resources together when they reference each other using hyperlinks.

A simple example follows with two resources, A and B.

Resource A:
  name: integer
  list_b: [list of resource B]

Resource B:
  id: integer
  description: String

Now, A does not include B in its document, but rather links to it. When using hypermedia, it might look something like this:

Resource A:
{
    id: 1,
    list_b: [
        { id: 1, href: "https://server/api/b/1" },
        { id: 2, href: "https://server/api/b/2" }
    ]
}

If a user wants to add or delete one of the B references in A's list, how do they do so, taking into account the presence of the hyperlink? I want the user to be able to update the entire A resource in one PUT operation, but nothing in the output indicates which value for B is required. It make sense to me for the user to perform PUT with content like this:

Resource A:
{
    id: 1,
    list_b: [
        { id: 1, href: "https://server/api/b/1" },
        { id: 2, href: "https://server/api/b/2" },
        { id: 3 },
    ]
}

and receive the updated resource (in the response) like this:

Resource A:
{
    id: 1,
    list_b: [
        { id: 1, href: "https://server/api/b/1" },
        { id: 2, href: "https://server/api/b/2" },
        { id: 3, href: "https://server/api/b/3" }
    ]
}

My concern is that the user won't necessarily know what to include in the resource when updating resource A's list_b.

When dealing with hyperlinks from one resource to another, how should creates and updates work? Should clients be allowed to update part of the link (the id), or should they be required to update both parts of the link?

Note: I know another approach might be exposing a sub-url for resource A. It could expose list_b as a resource which is operable via HTTP (allowing clients to use POST, PUT, and DELETE on the list resource itself). But this seems less reasonable when A contains multiple references to other resource types. Each field which references another would potentially require a sub-url, which, if there are 10+ fields, is unwieldy, and requires multiple HTTP requests to update the resource.

Was it helpful?

Solution

HATEOAS connects resources together in a RESTful interface, and it's not clear here whether or not the subsidiary objects you're describing really make sense as independent resources. The "AS" part of HATEOAS reminds us of the role that Web pages play as "resources" in a Web application. Each Web page is really an interactive representation of application state (the "application" in this case being a classical, multiple-page Web application), and the hyperlinks to other resources provide the user with transitions to other application states.

A RESTful Web API, having JavaScript code rather than human beings as its client, is naturally data-access-oriented, so few if any of its resources take the form of "application state," per se. In a tradition Web application, you can draw a state transition diagram and clearly see the connections among states, and thus among resources. In a RESTful API, the boundaries among passive data resources are motivated more by the efficiencies of client/server interactions and other subtle forces.

So do your subsidiary objects ("B") here really need to be represent as first-class resources? Are there instances where the front end will enumerate or otherwise access them independent of the aggregates in which they participate ("A")?

If the answer is "no," then they obviously shouldn't be represented hyptertextually in the "A" structure. I presume that the answer is "yes," however, and that you also have good reason to offer all of the other subsidiary objects to which you refer as independent resources. In this case, there's some amount of interface work in the form of routes and controllers that is necessary to support all of those resources no matter what, because your application is presumably providing a means to manipulate them each on their own, or at least query them (through hyperlinks such as those in your example).

This being the case, a POST to the path representing your collection of "B" objects (e.g., "server/api/b") can return a URL in the response's "location" header value as POSTs that create new resources are supposed to do. When your user interactively adds a new "B" to a list belonging to an "A" on your Web page, your front end can first POST the new "B," getting its URL back through the location header on success. It can then incorporate that link into the list representation inside its "A" object before PUTting the updated "A."

The ID value is a bit of a wrinkle, as you'll be tempted to break the encapsulation of the back end by extracting the ID value from the text of the URL. True HATEOAS zealots make their RESTful APIs produce obfuscated, hashed or otherwise unintelligible URLs specifically to frustrate such encapsulation-breaking on the part of clients. Better that the POST of the new "B" object returns a complete representation of the new "B" object, including its ID, in its response body, so that the client can reconstitute the full object and extract the ID from it, thus narrowing the coupling to the resource itself and not the details of the RESTful interface through which it is obtained.

OTHER TIPS

You should also look at the LINK method:

LINK /ResourceA/1 HTTP/1.1
Link: <http://example.com/ResourceB/3>; rel="list_b"
...

204 Yeah Fine, Whatever

This tells /ResourceA/1 to link to /ResourceB/3 using the relationship "list_b".

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top