granularity of link relations and embedding resources

Showing 1-15 of 15 messages
granularity of link relations and embedding resources Todd Perry 2/7/13 12:50 PM
I'm working on a REST service that exposes a fairly complex business domain and we're looking at using HAL, but there are a few questions I have about the best practices for link relations.

Many of our resources represent entities that have quite a lot of relationships... in some cases there could be multiple relations between the same resources.  This service is primarily for machine-to-machine usage (both server-to-server and in some cases by an AJAX client).

As an example, the "user" resource might have a home location, a current location, and a default location (this is a bit of a contrived example but it's close to what we might see).  Locations are also a resource. These relationships are all required.  In many/most cases these links might be to the same location resource.


What would be the best practice for the link relations?  I can see two options (we intend to use URLs for relations here and provide documentation at the URL, but I'm using simple strings for brevity):

- use a "location" relation for all 3 links, and use the name attribute (e.g. "home", "current", "default") to distinguish the links
- use three separate relations: homeLocation, currentLocation, defaultLocation

I'm leaning towards the second approach as being simpler and probably more correct since these ARE actually 3 different relationships.  But, I'm a bit concerned about having an explosion of rel URIs or CURIEs where we could be exposing different documentation links that all have identical information.  Of course, all these URIs that describe a relation to a "location" could lead to the same document for "location" resources...?

Regarding these relationships, I haven't seen much discussion of how to update resources where links are required.  These relationships are required, so any POST to create a new user in this example would need to supply a valid instance of all 3 location resources -- how does one typically differentiate between "read-only" links that simply link to related resources, and links that would be required in a POST/PUT?  Simply include that information in the documentation?

Most of our resources have at least one or two links to related resources that are required, so we'd be expecting at least one or two links in any PUT/POST.  I haven't seen any problem with doing this so far but was a bit worried that most examples and discussion seem to gloss over what happens when updating a resource that has "required" 1:1 or n:1 relationships with related resources.

Finally, I had some trouble when looking at embedding some resources.  We're using hal+json, and if we decide to use the name property on links, I don't see a way to provide the same information if we switch from using a link, to an embedded resource (typically for performance reasons).  The XML variant examples use a "name" attribute on the embedded resource element... but I don't see where to put something similar in the JSON structure.
Re: granularity of link relations and embedding resources Herman Radtke 2/7/13 1:35 PM
> - use a "location" relation for all 3 links, and use the name attribute
> (e.g. "home", "current", "default") to distinguish the links
> - use three separate relations: homeLocation, currentLocation,
> defaultLocation
>
> I'm leaning towards the second approach as being simpler and probably more
> correct since these ARE actually 3 different relationships.  But, I'm a bit
> concerned about having an explosion of rel URIs or CURIEs where we could be
> exposing different documentation links that all have identical information.
> Of course, all these URIs that describe a relation to a "location" could
> lead to the same document for "location" resources...?

I am not clear as to why the documentation between the three link
relations would be the same. Are you saying the response, a location
address, would be the same? What about the fact that a default
location means something different between the home location. I think
expressing the meaning of defaultLocaton is more important than
documenting the exact JSON response.

>
> Regarding these relationships, I haven't seen much discussion of how to
> update resources where links are required.  These relationships are
> required, so any POST to create a new user in this example would need to
> supply a valid instance of all 3 location resources -- how does one
> typically differentiate between "read-only" links that simply link to
> related resources, and links that would be required in a POST/PUT?  Simply
> include that information in the documentation?

Yes, I would include that information in the documentation.

At this point I have to wonder why defaultLocation, homeLocation and
currentLocation are different embedded items? Can you explain the
reason why a location response with that data in the response would
contain default, home and current locations.

--
Herman Radtke
@hermanradtke | http://hermanradtke.com
Re: granularity of link relations and embedding resources Todd Perry 2/7/13 1:55 PM

On Thursday, February 7, 2013 4:35:07 PM UTC-5, Herman Radtke wrote:
> I'm leaning towards the second approach as being simpler and probably more
> correct since these ARE actually 3 different relationships.  But, I'm a bit
> concerned about having an explosion of rel URIs or CURIEs where we could be
> exposing different documentation links that all have identical information.
> Of course, all these URIs that describe a relation to a "location" could
> lead to the same document for "location" resources...?

I am not clear as to why the documentation between the three link
relations would be the same. Are you saying the response, a location
address, would be the same? What about the fact that a default
location means something different between the home location. I think
expressing the meaning of defaultLocaton is more important than
documenting the exact JSON response.

I believe you're correct here.  I spent a little more time perusing the haltalk documentation and things became a bit more clear.

In this case there wouldn't be a lot to document about each relation except a sentence or two about the meaning of the relationship itself and a note (with link) that they all extend the "location" relation. 
 
At this point I have to wonder why defaultLocation, homeLocation and
currentLocation are different embedded items? Can you explain the
reason why a location response with that data in the response would
contain default, home and current locations.

My explanation was probably not clear without examples.  These are all links exposed from the "user" resource.  For example:

GET /users/1
{
  "_links": {
    "os:homeLocation": { "href" : "/locations/36" },
    "os:currentLocation" : { "href" : "/locations/16" },
    "self" .... etc
  }
}

The distinct description of each relation would be rather short:
 - homeLocation: the location where the user first registered, and which has administrative control of their account
 - currentLocation: the location where the user is currently located

Both of these are location resources, so documentation about the resource link itself (GET, PUT, POST, HTTP statuses, and request/response body) would be identical.

The haltalk example had something related to this involving the ht:author and ht:user relations, for example.  If there were an ht:location relation which most resources use ("the location of this resource") then perhaps these two relations might be documented as extensions of ht:location?
Re: granularity of link relations and embedding resources Herman Radtke 2/7/13 6:14 PM
>>
>> At this point I have to wonder why defaultLocation, homeLocation and
>> currentLocation are different embedded items? Can you explain the
>> reason why a location response with that data in the response would
>> contain default, home and current locations.
>
>
> My explanation was probably not clear without examples.  These are all links
> exposed from the "user" resource.  For example:
>
> GET /users/1
> {
>   "_links": {
>     "os:homeLocation": { "href" : "/locations/36" },
>     "os:currentLocation" : { "href" : "/locations/16" },
>     "self" .... etc
>   }
> }
>

Could you explain why the POST contains the user information plus the
location information but the GET is split into multiple requests? Is
the client able to update the current location after the user has been
created? I am just trying to understand the need for these many to one
required link relationships.
Re: granularity of link relations and embedding resources Todd Perry 2/7/13 6:49 PM

I didn't mean to imply that the GET was split into multiple requests -- the above GET example retrieves a representation of a single user, which includes his links to multiple locations.

To answer your last question, yes, the client is able to update both relationships:

PUT /users/1
{
  name: "Bob Smith",
  _links: {
      "homeLocation" : {href: "/locations/23"},
      "currentLocation" : {href: "/locations/23"},
      "defaultLocation" : {href: "/locations/23"}
   }
}

Would update the user including setting his home, current, and default location to /locations/23.  (I added the name property to make it clearer that user is a resource with properties as well as links to related entities.)

In this case, user is a resource that exposes a fairly simple domain entity.  So is location (each location could be a building, or a floor, department, etc).    I didn't mean to imply the GET was split. 

User is an entity/resource with several properties (name, address, etc), and three separate 1:n relationships with the location entity (home location, current location, default location).

GET /users/1 retrieves a single user
PUT /users/1 updates a single user
POST /users creates a new user

GET /locations/16 retrieves a single location
PUT /locations/16 updates the state of location 16
POST /locations adds a new location

/locations/16 also happens to be the home location of that user as my sample links indicate.

What I was struggling with is that in the above example, the documentation for os:homeLocation is going to be slightly different than os:currentLocation; but both relations point to a location resource, so all the documentation for the GET/PUT/DELETE methods, as well as the request bodies will be the same.

For instance, any of the location link relations on the user are going to describe exactly the same methods for updating those locations:

PUT /locations/23
{
  "name": "Building A",
  "hasShippingOffice": true,
  "address": ...
}

as well as the HTTP status codes, etc.

I think I was getting too hung up on this issue... as basically these relations can all have their own documentation that simply states the purpose of the relation and then refers to the "basic" "os:location" link relation.  There will be at least one of these link relations in the resource that lists all the available locations:

GET /locations
{
  description : "All locations",
  _links: {
     "os:location": [
           { href: "/locations/1", title: "Home office" },
           { href: "/locations/23", title: "Building A" },
           { href: "/locations/32", title: "Building F" }
       ...etc
      ]
    }
}

The os:location CURIE above will probably be where we document the GET/PUT/DELETE methods for a location.
Re: granularity of link relations and embedding resources Herman Radtke 2/7/13 7:46 PM
> PUT /users/1
> {
>   name: "Bob Smith",
>   _links: {
>       "homeLocation" : {href: "/locations/23"},
>       "currentLocation" : {href: "/locations/23"},
>       "defaultLocation" : {href: "/locations/23"}
>    }
> }

I have never seen anyone pass the links down in the PUT request. The
links are representations of a different resource. I think that the
root of the problem you are facing. My approach would be to figure out
a way to correct this dependency between resources.

This is starting to get into more of a two-way discussion though. Feel
free to ping me on irc (hjr3 on freenode) if you want to discuss this
some more. I will let someone else chime in on the list and see what
they think.
Re: granularity of link relations and embedding resources Joost Cassee 2/7/13 7:55 PM

The discussion may have turned into a dialog, but as a real-world example it is very interesting to follow. I like listening in on it. If you do take it private, please post a summary of your conclusions on the list.

Joost

Op 8 feb. 2013 04:46 schreef "Herman Radtke" <herman...@gmail.com> het volgende:
--
You received this message because you are subscribed to the Google Groups "HAL Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to hal-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


RE: granularity of link relations and embedding resources JC 2/7/13 8:06 PM

I agree.

 

I’m contemplating something similar. 

Assume the user object has attributes for gender and birthDate.

I’m interested in being able to do an update to Bob and pass in gender, birthdate, and currentLocation.

It would be great if I can do that in a single request using _embedded.  In this case, I might not even know the self links for the address resources.

 

 

 

From: hal-d...@googlegroups.com [mailto:hal-d...@googlegroups.com] On Behalf Of Joost Cassee
Sent: Thursday, February 7, 2013 10:56 PM
To: hal-d...@googlegroups.com
Subject: Re: granularity of link relations and embedding resources

Re: granularity of link relations and embedding resources Mike Kelly 2/7/13 11:18 PM

Hi Todd,

I think you're spot on here. Have the more specific location relations extend the generic by using a link in the docs.

As long as its all comprehensible by a client dev then your Doing It Right. As you mention I took that approach with haltalk, and people seem OK with it.

Please keep us updated on your progress! What are you working on?

Cheers,
M

--
You received this message because you are subscribed to the Google Groups "HAL Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to hal-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 
Re: granularity of link relations and embedding resources Herman Radtke 2/7/13 11:19 PM
> I’m contemplating something similar.
>
> Assume the user object has attributes for gender and birthDate.
>
> I’m interested in being able to do an update to Bob and pass in gender,
> birthdate, and currentLocation.
>
> It would be great if I can do that in a single request using _embedded.  In
> this case, I might not even know the self links for the address resources.

Mike Kelly can correct me if I am wrong, but I have understood Hal to
be focusing on the response. The details of how a client should POST
and PUT are hand waved away into the documentation. I think this was a
good decision. It is one thing to write a client that understands a
response that is of the media type application/hal. It is entirely
another thing to write a client that understands how to construct a
request using the application/hal media type.

I think the main problem with a POST that includes embedded data is
how you handle the response. Remember that something embedded is being
treated as a different resource. Let's look at the above example of a
user with a gender, birthdate and embedded location.

POST /users

{
    "gender": "f",
    "birthdate": "1980-01-01",
    "_embedded": {
        "currentLocation": {
            "location": "Los Angeles"
        }
    }
}

What happens when the new user resource passes validation, but the
embedded location data fails validation? If these were two separate
requests, the first one would return a 201 response with the newly
created member and the second would return a 400 response with an
error message. I think trying to return a single response for this
becomes almost impossible to get right.

Let's change strategies then and move the location out of the _embedded section.

POST /users

{
    "gender": "f",
    "birthdate": "1980-01-01",
    "currentLocation": "Los Angeles"
}

This POST request is fine because the operation is atomic and the
server can send back a normal HTTP response. Now from what I
understand of the emails, the desired response looks something like
this:

{
    "_links": {
        "self": { "href": "..." }
    },
    "gender": "f",
    "birthdate": "1980-01-01",
    "_embedded": {
        "currentLocation": {
            "location": "Los Angeles"
        }
    }
}

The problem now becomes how the client is supposed to construct the
PUT request to update the user. If it is supposed to include the
location information then why are we treating location as a different
resource?

Now it was mentioned that we treat it as a different resource because
we want to update it independent of the user information in addition
to requiring it when updating the user. Why not use PATCH (or just do
what everyone does and send a partial PUT request) with only the
location information to the user?

Update just the users location:
PUT /users/1

{
    "currentLocation": "Las Vegas"
}

Update the entire user:
PUT /users/1

{
    "gender": "m" /* what happens in Vegas... */
    "currentLocation": "Las Vegas"
Re: granularity of link relations and embedding resources Mike Kelly 2/7/13 11:32 PM


On 8 Feb 2013 07:19, "Herman Radtke" <herman...@gmail.com> wrote:
>
> > I’m contemplating something similar.
> >
> > Assume the user object has attributes for gender and birthDate.
> >
> > I’m interested in being able to do an update to Bob and pass in gender,
> > birthdate, and currentLocation.
> >
> > It would be great if I can do that in a single request using _embedded.  In
> > this case, I might not even know the self links for the address resources.
>
> Mike Kelly can correct me if I am wrong

Nope - no need :)

Re: granularity of link relations and embedding resources Joost Cassee 2/7/13 11:37 PM
I usually solve this problem by returning application/hal+json for
GETs (and application/json if requested), but requiring
application/json for PUTs. That way only the state of the resource is
updated and not the links or embedded resources. Furthermore, the full
application/json representation is sent, fulfilling the requirement
for PUT; no need for PATCH.

Regards,
Joost

2013/2/8 Herman Radtke <herman...@gmail.com>:
> --
> You received this message because you are subscribed to the Google Groups "HAL Discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to hal-discuss...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>



--
Joost Cassee
http://joost.cassee.net
Re: granularity of link relations and embedding resources Todd Perry 2/8/13 8:29 AM
There's a fairly rich domain model below this service (it's an existing system, not a new one.  Only this service layer is new, although it might drive some changes in the underlying model.  The relationships between entities are considered part of the state of the entities that "own" them -- in this case the associations with the locations are part of the user's model.  Most n:1 relationships belong to the "1" side.  

It's not really going to be possible to invent some way to update the link relationships separately; they're often required, especially when creating a new entity/resource.  And almost all of our model entities have several relationships.  As far as the underlying entity model is concerned the home location is just a field on the user and is not really any different than the name - both are required to exist.

We did consider the issue of updating relationships and whether it was a good idea to use links for this, but it did seem to go against the grain of HAL to use links as regular JSON properties.   It hadn't occurred to me until this discussion, that we might want to avoid using HAL for updates and use regular JSON.

One of my concerns about that is that it would put an extra burden on the client developer.  Although we are developing our own client for this service, it's also going to be published for our customers and they are going to be writing their own client applications.   Our decisions about validation has been driven mostly by the concern about being lenient and keeping the interface simple -- so the decision was to allow, for example, a client to submit for PUT the same representation they just retrieved for a GET, even if some of the properties in the original resource aren't updateable -- this includes related links that don't represent relationships (such as search templates or links that are provided merely for navigation of the service).

Requiring a different, possibly raw JSON request for updates where would mean that the client has to generate a new representation.  I assume it would be something like this, treating the relationship href as an opaque identifier (we don't want to expose raw identifiers or deconstruct links in this service):


PUT /users/1
  {
  "name" : "Bob Smith",
  "homeLocation" : "/locations/16",
  "owningLocation" : "/locations/16",
...
  }

I noticed some other replies to this discussion -- I'd really like to hear the pros and cons for both these approaches, and especially like to hear from anyone who's had to develop something similar.  We're still in the prototyping stage and we'd like to get it right before beta testing starts.  :-)
Re: granularity of link relations and embedding resources David Haslem 2/8/13 9:34 AM
For relationships, I've always fallen back to passing ids back and forth
in representations, which seems very wrong for HAL but felt easier
(/more familiar) than trying to figure out something better:

    {
      "id": 1,
      "home_location_id": 16,
      "_links": { "home_location": { href: '/locations/16'} }
    }

Passing the urls as a property you can update seems just as easy, and
semantically it feels better than exposing raw database ids. I think
I'll probably take that approach for future projects. In the case it is
a property that can be updated, I'd probably still show it at the top
level in GETs (making it easier to build an updated version for a PUT -
just drop the _links and _embedded keys), in addition to including an
href inside of _links.

Trying to do a PUT with HAL as the media type seems like it'd be
potentially troublesome, since as you mentioned, many of those links
might be informational and not updatable.

The more I think about it, the more it makes sense to me that you'd
duplicate the relationship as both a link and an opaque top level
property. The relationship itself is a property of the current resource,
and so should be top level. That you can follow that uri to another
resource is just a bonus, and you indicate the ability to do that with
the href for it in _links.

I suppose the downside is this might encourage a bad habit of people
relying on being able to dereference that top level link, but hopefully
documentation indicating that the official link for traversal will
always reside in "_links" should discourage that.

- David
Re: granularity of link relations and embedding resources Todd Perry 2/8/13 10:29 AM
Mike,

This is a library automation system - this service is actually a small slice of the entire system, related to the circulation desk.  We're developing the services as smaller modules.  We have other teams working on a client prototype (actually two; a web application and some m2m back ends for other legacy services).  Having actual consumers for our prototype has led us to some design issues like this one fairly early in the process.

I didn't realize immediately that the haltalk service was using application/json rather than hal+json for updates... I missed that part of the documentation my first two times through it.  :-)  I assume that means you're generally of the opinion that HAL isn't necessary (or convenient?) for updates?

Having the entity relationships be part of the resource state as a regular JSON property sits okay with both our teams.  I think the client team prefers it, actually.  One of the concerns they had already raised was the issue with mixing different "types" of links in the representation.  Except for documentation there was no clear way to distinguish a link that was actually a (possibly required) entity relation from an informational link, unless the link was a template, which would pretty obviously make it part of the service rather than part of the resource state.

That does raise another question, though -- whether it's a good idea to also include those links as regular properties in the HAL representation from a GET.  David suggested in another part of the thread, the possibility of including the relationship as a link (for navigation purposes) but also as a property.

The client team has expressed some interest in this idea - I think some developers are uncomfortable with having to dig through the link hash for parts of the resource state, when they need access to all the properties of a user, for instance.  We understand the need of having it as a link and don't want to remove it from the links, but having it as part of the main body (IF it's an actual entity relationship) allows them to keep the "link handling" separate from the "resource state" handling.    It also make it easier to build a update representation by starting with the original HAL response (sans links and embedded entities).

But I think there was a discussion about this a while back, and the consensus seemed to be against this approach.