Search code examples
jsonresthateoashal

Removing an item from a collection in a HATEOAS way


I am working on a project whose client uses REST/HATEOAS to get and modify data.
Here is a part of the class diagram: class diagram.

A hal+json HATEOAS response of the request /group/12345 would look like:

{
  "id": "12345",
  "_links": {
    "self": {
      "href": "/group/12345"
    },
    "roles": {
      "href": "/group/12345/roles"
    }
  }
}

To remove a role from this group I could simply execute a DELETE on the specific role. (because every role has to be in exactly one group and moving a role to another group shouldn't be allowed).
So I would add a link with the rel "drop" to the role. Therefore the client knows if or rather when a DELETE request is allowed:

{
  "id": "67890",
  "_links": {
    "self": {
      "href": "/roles/67890"
    },
    "users": {
      "href": "/roles/67890/users"
    },
    "drop": {
      "href": "/roles/67890"
    }
  }
}

So to delete a user the client looks for the drop link. If no link is found, the delete is not allowed. Otherwise it executes a DELETE request on the found link.

But what should I do if I want to remove a user from a role?
I cannot simply delete the user. The role -> user relation is not an aggregation.
How can I tell the client if removing a user from a role is allowed?

To remove a user from role I would use DELETE /groups/12345/roles/67890/users/ABC. And to delete the user I would use DELETE /users/ABC.

So where should I put the "remove user from role" link?

Thank you in advance :)


Solution

  • Links typically have three parts

    • A context identifier
    • a link relation type
    • a target identifier

    Frequently, the context identifier is implied by the... umm... context, rather than being made explicit.

    From what I can see, hal+json doesn't have a mechanism for explicitly specifying the context identifier, which would mean that you need to rely upon the implicit approach.

    That suggests that if you want to be communicating about removing a user from a role, then you need a context that implies the right identifier.

    You would normally do this in one of two ways

    • have a chain of links that leads to a representation of the user in that role, and include the drop role link relation in the list of links
    • embed a representation of the user in some other representation, and include the drop role link within that embedded resource.

    For example, you might have a representation of /roles/67890/users that looks something like

    _links: {
        self: {
            href: /roles/67890/users
        }
    }
    
    _embedded: {
        users: [ {
            _links: {
                self: /users/ABC
                /role/remove :  /groups/12345/roles/67890/users/ABC
            }
       } ,
       ... ]
    }