1

Let's say I have a REST API adhering to basic HATEOAS principles. Items belong to a User.

GET /item/13

{ 
  id: 13,
  name: 'someItem',
  type: 'someType',

  _links: [
    {
      rel: 'user',
      href: '/user/42'
    }
  ]        
}

Now I need a way to change the user for a given item. Using either a PUT or a PATCH, which is the preferable way of performing that modification?

  1. Establish the new relation by setting the id of the new linked resource as a simple property in the JSON body

    PATCH /item/13
    {
      userId: 43
    }
    
  2. Establish the new relation by having the client pass the link itself as the input

    PATCH /item/13
    {
      _links: [
        rel: 'user',
        href: '/user/43'
      ]
    }
    

I usually think of links as read-only representations of relations stored in other formats (such as id:s to other resources), returned from GET calls. It doesn't feel very natural to me to have links as input to POST/PUT/PATCH calls, and the fact that links is an array makes it even stranger (should you be able to update all links? One single link?), but I have seen it suggested in various articles. Is there a best practice? What would be the benefits of using the links approach?

JHH
  • 8,567
  • 8
  • 47
  • 91
  • 1
    for such scenarios I always like how GitHub uses put on child resource when starring project. I their API you use `PUT` or `DELETE` on `/project/star`. For simple modifications I usually go with PUT `/item/{ID}/user` or DELETE `/item/{ID}/user/{ID}`. It seams to be not compliant with REST standards but in my opinion is less awkward to use than passing it in main resource body requests. – Adam Jan 26 '16 at 09:36

1 Answers1

4

The point of REST is (at least one of them) is to make everything visible through a standard interface. In other words, if the 'relations' are a thing, than it too should have its own resource.

The API should also be more descriptive. This might be subjective, and I don't know all the details of your model/design, but 'items' don't have 'links'. 'Items' might instead have a single 'owner'. If this is the case, it might look something like:

GET /item/123/owner

So POSTing or PUTing an URL of a user (or some simple representation) would 'change' the owner of the item. It might be not allowed to DELETE the owner, depending on if the model allows unowned items.

Note, that the representation under "/item/123" would in this case have to link to "/item/123/owner", since the client only follows links it gets from the server.

So, think about what are important 'things', all of those should have a resource. Also, try to add as much 'meaning'/semantics as you can. The relation should not be called 'user', it should be called 'owner' (or whatever the meaning should be in your model).

Robert Bräutigam
  • 7,514
  • 1
  • 20
  • 38
  • Really good advice, thanks, this makes sense. I will try to implement this. In reality, of course my model is much more complex than the simplfied example shown above. – JHH Jan 26 '16 at 11:40
  • One thing that hits me is how to handle initial values of owner though? If an owner *may* be present already when items are created, it doesn't seem ideal to have to do a `POST /items` immediately followed by a `PUT /items/xx/owner`. – JHH Jan 26 '16 at 11:45
  • In some cases the needed relations can be expressed through the URI itself (e.g. perhaps you wouldn't do a `POST /items` but a `POST /users/joe/items` if you know the item initially belongs to Joe?) but there are situations where this doesn't make sense, you might want to "anchor" the item to some other kind of entity than the owner for example. In this case I see no other option than having a `owner: 'joe'` inline with the other item properties in the body of the `POST /items`? – JHH Jan 26 '16 at 11:45
  • Yes, if an 'owner' is a part of an item, then the item representation you POST should have the 'owner' filled. Note however, this should not be 'joe', it should be a URI linking to the owner if you can manage it, this makes the whole system easier for clients (since the clients natively work or at least should work with URIs), and makes the system more loosely coupled. – Robert Bräutigam Jan 26 '16 at 11:59
  • Ah ok. My example having 'joe' as the value was a poor choice; I did not intend the value to be a "name", but rather a resource id. So it could be 42, 72f4bac892ee023c7 or whatever. My current client unfortunately does use id:s in some cases where URI:s would make more sense. In any case I see your point that it should rather be /users/42. – JHH Jan 26 '16 at 12:07