Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should HATEOAS-style links be implemented for RESTful JSON collections?

Tags:

json

rest

hateoas

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:[         { 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?

like image 476
thomas-peter Avatar asked Jul 26 '13 09:07

thomas-peter


People also ask

What is HATEOAS links?

HATEOAS stands for Hypermedia as the Engine of Application State and it is a component of RESTful API architecture and design. With the use of HATEOAS, the client-side needs minimal knowledge about how to interact with a server.

What are the advantages of the HATEOAS for RESTful API?

Using HATEOAS allows an API to clearly define a control logic that is available at the client-side. This enables them to follow links embedded in the API resources instead of having them manipulate URLs. This decouples clients from the URL structure so that changes don't end up hurting integration.


1 Answers

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.

like image 145
Nicholas Shanks Avatar answered Sep 28 '22 07:09

Nicholas Shanks