To keep things simple and to avoid nameing collisions, I've been bundling links in my record resources like this...
{
id: 211,
first_name: 'John',
last_name: 'Lock',
_links: [
{ rel: 'self', href: 'htttp://example.com/people/211' }
]
}
However, I can't work out how to implement links in collections. I have spent a long time trawling around the web for examples and other than using the not so lean HAL I'm unable to reconcile my problem.
[
{id:1,first_name:.....},
{id:2,first_name:.....},
{id:3,first_name:.....},
"_links": "Cant put a key value pair here because its an-array"
]
Which means I have to wrap up the array in a container object.
[
people: [ {id:1,first_name:.....} ],
links: [ { rel:parent, href:.... ]
]
But is is different to the singular resource so I'm going to make the record behave like the collection and wrap it up in a container....
{
person: {
id: 211,
first_name: 'John',
last_name: 'Lock',
_links:
},
links:[
{ rel: 'self', href: 'htttp://example.com/people/211' }
]
}
On the surface this seems like quite a neat solution. The resulting JSON is one level deeper but HATEOAS has been implemented, so that's all good right? Not at all. The real sting comes when I go back to the collection. Now that the single resource has been wrapped up in a container in order to be consistent with the collection, the collection must now be changed in order to reflect the changes. And this is where it gets ugly. Very ugly. Now the collection looks like this...
{
"people": [
{
"person": {
....
},
"links" : [
{
"rel": "self",
"href": "http://example.com/people/1"
}
]
},
{
"person": {
....
},
"links" : [
{
"rel": "self",
"href": "http://example.com/people/2"
}
]
}
],
"links" : [
{
"rel": "self",
"href": "http://example.com/people"
}
]
}
Is there a simpler solution to implementing HATEOAS for collections? Or should I kiss HATEOAS goodbye for forcing me to over complicate the data structure?
Please don't dismiss HAL so quickly just because it looks a little bloated (in its JSON form, it's quite minimal).
HAL is to JSON what HTML is to plain text.
It adds hyperlinks. You need hyperlinks and a commonly understood representation format (such as HAL or Collection+JSON) for REST. You also need HATEOAS for REST, without HATEOAS it isn't REST! HATEOAS requires hyperlinks of course.
In your case, you are trying to build a collection resource. The IANA-registered relation for that is "item" (with reverse relation "collection"). Here is the representation in HAL for a People collection:
{
"_links": {
"self": { "href": "http://example.com/people" },
"item": [
{ "href": "http://example.com/people/1", "title": "John Smith" },
{ "href": "http://example.com/people/2", "title": "Jane Smith" }
]
},
"_embedded": {
"http://example.com/rels#person": [
{
"first_name": "John",
"last_name": "Smith",
"_links": {
"self": { "href": "http://example.com/people/1" },
"http://example.com/rels#spouse": { "href": "http://example.com/people/2" }
}
},
{
"first_name": "Jane",
"last_name": "Smith",
"_links": {
"self": { "href": "http://example.com/people/2" },
"http://example.com/rels#spouse": { "href": "http://example.com/people/1" }
}
}
]
}
}
Note:
The primary data for this collection comes from _links.item[]
. These are the items in the collection. The full (or at least some additional) data for each item is available in the _embedded
array. If the client needs these additional data, it must find them by searching through _embedded[n]._links.self.href
for each n
. This is a design constraint of HAL. Other hypermedia representation formats have similar constraints (though perhaps going in the other direction).
I have added a title
value for each member of the item
array. This can appear between the opening and closing anchor tags if rendering to HTML, or as the text of a menu item in the client, without need for further processing of the representation by the client.
There are no ID parameters. All references to other resources are exposed as hyperlinks. A client should not have to "build" a URL by gluing an ID into a URL at some pre-defined place. This constitutes out-of-band information which inhibits independent changes to the client and server.
All of your hyperlinks should be absolute, since relative URLs may cause problems. All of your relations should be either listed on that IANA page or use a URI to define them. Ideally, that URI should be a dereferencable HTTP URL with documentation about the relation at the other end.