Self vs. related links (and what to return)

I’m always having a difficulty to grasp the difference between self and related links.

The spec uses this example:

{
  "type": "articles",
  "id": "1",
  "attributes": {
    "title": "Rails is Omakase"
  },
  "relationships": {
    "author": {
      "links": {
        "self": "http://example.com/articles/1/relationships/author",
        "related": "http://example.com/articles/1/author"
      },
      "data": { "type": "people", "id": "9" }
    }
  },
  "links": {
    "self": "http://example.com/articles/1"
  }
}

The self link of the main resource’ article nicely points to http://example.com/articles/1

Now assuming that the author relationship effectively means a person (people resource) and is available via http://example.com/people/9

Should the self or related link of the author relationship not point to http://example.com/people/9 ?
I really wonder what in the example http://example.com/articles/1/relationships/author (self link) and http://example.com/articles/1/author (related link) should return.

1 Like

Its my understanding that self relates to the context of the request which generated the document, and related is the canonical URL of the resource.

Yes, that’s also how I read the specs.

That being said, I think the example from the spec might be very confusing.

Instead of (copied from the specs):

{
  "type": "articles",
  "id": "1",
  "attributes": {
    "title": "Rails is Omakase"
  },
  "relationships": {
    "author": {
      "links": {
        "self": "http://example.com/articles/1/relationships/author",
        "related": "http://example.com/articles/1/author"
      },
      "data": { "type": "people", "id": "9" }
    }
  },
  "links": {
    "self": "http://example.com/articles/1"
  }
}

I would expect something like:

{
  "type": "articles",
  "id": "1",
  "attributes": {
    "title": "Rails is Omakase"
  },
  "relationships": {
    "author": {
      "links": {
        "self": "http://example.com/articles/1/relationships/author",
        "related": "http://example.com/people/9"
      },
      "data": { "type": "people", "id": "9" }
    }
  },
  "links": {
    "self": "http://example.com/articles/1"
  }
}

where the canonical URL of the author’s related links makes more sense imo.

Anyway, am I correct to assume that if a client both the self and related link (of the author relationship) return the same response? And to be more specifically a people resource.

Ahhhhh I see where you’re held up. This is some historical context dragged in through Ember/Rails which dictates the URL patterns for sub -> related resources. They didn’t want to have it fully alien to the community, so this was how it was displayed in the spec.

As a better designed API I would also expect /people/9 would be the canonical URL, and I’ve made that point in the past as well. So, in reality the only confusion you have is with the bad example provided in the spec.

Yes indeed :slight_smile:

And it might be arguable that the self link of the relationship link is not the canonical URL.
But I guess that is another discussion :slight_smile:

Anyway, the assumption that both the self and related link return the People resource in this case is correct?
Something like:

{
  "data": { 
    "type": "people",
    "id": "9",
    "attributes": { .. },
    "links": {
      "self": "http://example.com/people/9"
    }
 }
}

Note (to clarify) GETting http://example.com/articles/1/relationships/author in this case just responds with above json, without any further reference to http://example.com/articles/1/relationships/author itself, right?

The relationship link is the canonical URL of the relationship which would be /articles/relationships/author in this case. It’s essentially a resolvable URL which represents the semantic link between two resource sets (many->one, one-> one, one->many, many->many). It has to be somewhere, and there really isn’t any issue I see with that structure as a convention.

Regarding the people resource question, in the example the author is a sub-resource of the article.

In the example you set up, yes GET /articles/1/relationships/author would return that response, but the self link would return the context which generated the document, and the related link would return the canonical URL you have listed in self currently.

Hi Michael,
Yes I see what you mean regarding the self link. Thx for your patience!

Just for reference

So in this case the related link http://example.com/people/9 will return:

{
  "data": { 
    "type": "people",
    "id": "9",
    "attributes": { .. },
    "links": {
      "self": "http://example.com/people/9"
    }
  }
}

and the self link http://example.com/articles/1/relationships/author will return:

{
  "data": { 
    "type": "people",
    "id": "9",
    "attributes": { .. },
    "links": {
      "self": "http://example.com/articles/1/relationships/author",
      "related": "http://example.com/people/9"
    }
  }
}

Is it correct to assume that the latter then would contain a related link then just in the article document?

If I may, after doing some research on the subject myself, I came across this explanation of @dgeb in a (rather old) post on github:

A self link for a relationship is called the “relationship URL” and should return the relationship’s linkage data - i.e. the resource identifier object(s) associated with the relationship. Requests can also be sent to the relationship URL to modify the relationship.

A related link for a relationship is called the “related resource URL” and should return the resource(s) currently in the relationship.

Given that, you should use related in your links (e.g. “https://api.example.com/compliance/facilities”) to return full resource objects.

The way I interpret this, together with the specs, I would come to the following request/responses based on your example:

The self link http://example.com/articles/1/relationships/author will return:

{
  "data": { 
      "type": "people",
      "id": "9"
  }
}

Whilst the related link in that case should be http://example.com/articles/1/author, which would return the entire resource object:

{
  "data": { 
    "type": "people",
    "id": "9",
    "attributes": { .. },
    "links": { 
      "self": "http://example.com/people/9"
    }
  }
}

Does that make sense?

Hi Maarten,

Thanks for you reply. I think it makes sense!

Looking at specs (again) I think indeed the relationship self link (“relationship URL”) should return as you suggested just the resource linkage (id + type).
The specs state:

The primary data in the response document MUST match the appropriate value for resource linkage, as described above for relationship objects.

Regarding the relationship related link (“related resource URL”) I think we all agree (and as stated by @dgeb) it should return the full resource.

Whether the self link of that document should be the related link or the the actual resource url (http://example.com/articles/1/author vs http://example.com/people/9 in this case) is not clear in the specs and so debatable (unfortunately). Maybe @dgeb can shed some light on this.

Hi Marcel,

Hm, I see what you mean. According to the docs, the top level links member should be:

The top-level links object MAY contain the following members:

self: the link that generated the current response document.
related: a related resource link when the primary data represents a resource relationship.
pagination links for the primary data.

So in the top level, the self link should be the link you suggest (http://example.com/articles/1/author). Indeed the spec does not state anything about the self link in the resource object.

Also, the statement about the related link in the top level link member confuses me:

related: a related resource link when the primary data represents a resource relationship.

By resource relationship, I suppose what is meant is when the primary data is a Resource Identifier Object (or a collection of Resource Identifier Objects), instead of a full Resource Object. How did you interpret this?

I agree, it would really help if @dgeb or one of the other spec authors could clarify things.

Hi @marceloverdijk and @maartenzwart,

I’ll do my best to clarify, although at a glance it sounds like you’ve worked through everything.

If you follow a relationship’s related link, then the document that’s returned should include full resource(s) in data. The top-level self link should match the related link that was followed to generate the document.

If you follow a relationship’s self link, then the document that’s returned should include resource identifier(s) in data. The top-level self link should match the self link that was followed to generate the document. The top-level related link should return the full resource(s) in data.

Within each resource, the self link should return that full resource in data.

Let me know if anything is still unclear.

1 Like

Thanks for clarifying @dgeb

To fully summarise this thread for future readers, imagine having an articles document like:

{
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON API paints my bikeshed!"
    },
    "links": {
      "self": "http://example.com/articles/1"
    },
    "relationships": {
      "author": {
        "links": {
          "self": "http://example.com/articles/1/relationships/author",
          "related": "http://example.com/articles/1/author"
        },
        "data": {
          "type": "people",
          "id": "9"
        }
      }
    }
  }
}

In this case following the author relationship self link (http://example.com/articles/1/relationships/author) should return:

{
  "data": {
    "type": "people",
    "id": "9"
  },
  "links": {
    "self": "http://example.com/articles/1/relationships/author",
    "related": "http://example.com/articles/1/author"
  }
}

Following the author relationship related link (http://example.com/articles/1/author) should then return:

{
  "data": {
    "type": "people",
    "id": "9",
    "attributes": {
      "first-name": "Dan",
      "last-name": "Gebhardt",
      "twitter": "dgeb"
    },
    "links": {
      "self": "http://example.com/people/9"
    },
  },
  "links": {
    "self": "http://example.com/articles/1/author",
  }
}

Let me know if you think something is wrong with this.

5 Likes

Everything looks good except that the related link in the document returned from http://example.com/articles/1/relationships/author should be http://example.com/articles/1/author not http://example.com/people/9.

Thx again, I’ve edited the previous post (to avoid confusion for readers).

1 Like

Thanks @dgeb for clearing this up, really helpful.

Nice summary @marceloverdijk!

1 Like

Excellent summary. I wonder if it may be worthwhile to submit a pull request to fix the examples to move them away from the ember conventions as they seem to require frequent explanation.

1 Like

Yes I agree that would be great.

Sorry to revive an old discussion. This is more of an implementation question on the related link topic and please let me know if I need to start a new discussion, but I thought this would be appropriate.

As confirmed by dgeb, if we follow the http://example.com/articles/1/author related link the response should include the full resource. But lets say, we have a microservice architecture and the articles service only has reference to the author. The full resource attributes of author would only be present in the people service. How do you think that the articles service handle the related link as the people attributes should be the responsibility of people service? And if the related link was http://example.com/people/9 there would not be any issue with the implementation at all.

2 Likes

I’m finding this fairly confusing as well, especially since the specification emphatically states that the URL structure is not governed by the specification. FWIW, I have no background with Rails.

There are two cases that are meaningfully distinct:

  • the related author is a sub-resource of the article, and is not shared with other articles or uses of people resources, and

  • the author is a reference to an independent resource that is (or could be) managed separately.

In the first case, I would expect http://example.com/articles/1/author could be allowed to return a redirect to a canonical location for the resource. Whether this should be a permanent (308) or temporary (307) redirect would depend on whether the relationship can be edited.

If the author resource is not wholly owned and controlled by the article resource, it’s not at all clear that returning "http://example.com/articles/1/author" as the related link is required, since the stated semantic is only that the link points to the related resource. If that was the intent, I don’t see where that’s spelled out in the specification.

1 Like