-
Notifications
You must be signed in to change notification settings - Fork 890
Description
Please find below a rewritten #622, intended to start post-1.0 discussion. I think that this is compatible with #675.
One of the main points in the discussion in #622 was where to place RFC relation name, to links member keys or inside link objects. IMHO, both options have strong arguments in favor of them, and I think they have distinct use cases. Forcing a given option in a use case suitable for the other option is not likely to satisfy everybody, so I am suggesting to support both, see more detailed justification below. Likewise, arrays of link objects have a strong use case.
Proposed changes
- Each member of a links object can be either a link object or an array of link objects.
- Each member of a links object can be keyed with either of
- RFC5988 registered relation type. All current link names are of this category.
- RFC5988 extension relation type, i.e. an URI.
- A custom link name: a string prefixed with something, I suggest double dash (
--
) for this purpose. If defined this way, it- follows the style of existing registered relation names,
- does not conflict with
reg-rel-type
naming rule from RFC5988, - keeps these links easily identifiable as custom links,
- keeps them as readable as natural language words/phrases.
- In the cases (2)(i) and (2)(ii), the list of possible members of links object is
href
: required, self-explanatory,method
: optional, contains HTTP verb,- members keyed by names of
link-param
s from RFC5988 or its extensions: optional (alternatively, list them underrfc5899
member, as proposed in Links, relationships, discoverable actions, mapping to RFC5988 #622).
- In the case (2)(iii), an additional member of link object can be provided by the server:
rel
: array, optional, contains RFC5988 relation names, either registered or extensions.
- (2)(i) and (2)(ii) cover the cases when the representation of links in a given API is driven by semantics of RFC5988.
- (2)(iii) covers the following use cases, which cannot be easily covered by (2)(i) and (2)(ii):
- RFC5988 allows to list multiple relations for a given link: having them all listed in links object key (as CSV or space-separated) would be awkward,
- when API has multiple links for a given relation name, but for some reason prefers not to group them into an array by relation name,
- when API prefers to identify links by distinct
name
rather than relation name, and to apply relation name to a group of links, - custom links, suitable for cases when the API is not registering relation names with IANA or does not care about IANA registry at all, but still prefers to use plain words for link names.
- Comparing to Links, relationships, discoverable actions, mapping to RFC5988 #622,
class
is dropped: I still think it would be useful, but in this framework it becomes a fully additive feature, it can be considered later,- equivalent for
name
is only available in case (2)(iii), as it is has no match in RFC5988.
- Dedicated issue: Links: a relation for actions #745. JSON API should register with IANA a relation to identify actions, as AFAIK there is no existing relation for this yet. The motivation is that the client should have a way to extract all actions from the list of links for a given resource. Suggestion for relation name:
action
. - Dedicated issue: Suggestion: "target", a reserved member of relationship linkage #477. For relationship-level
links
, define a memberitem
(another name could be used, such astarget
,related-target
, etc., but it should be registered with IANA) to list target URLs of the relationship. - Dedicated issue: Links: home endpoint for browsing an API #746. JSON API should register with IANA a relation to communicate starting point for browsing a HATEOAS-compliant API in a top-level link. Suggestion for relation name:
home
(was classexplore
in Links, relationships, discoverable actions, mapping to RFC5988 #622). - Dedicated issues: Links: a relation for links handling content hierarchies #747, Links: a relation for links handling pagination #748, Links: a relation for links handling content versioning #749. Other relations can be registed with IANA, e.g.,
pagination
,versioning
(see explanation for the corresponding classes in Links, relationships, discoverable actions, mapping to RFC5988 #622).
Example
Illustrates all formats, both simple cases (article with ID=3) and complex cases (article with ID=2), dynamic permissions through actions (published article with ID=1 vs unpublished article with ID=2), RFC5988 mapping, custom links (report for article with ID=1), item
as array and as a string.
GET /articles?page=1&page_size=3&related_page_size=2
{
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON-LD paints my bikeshed!",
"published": "true"
},
"relationships": {
"authors": {
"links": {
"self": "http://example.com/articles/1/links/authors",
"related": "http://example.com/articles/1/authors",
"item": [ "http://example.com/authors/3", "http://example.com/authors/4" ]
},
"data": [
{ "type": "authors", "id": "3" },
{ "type": "authors", "id": "4" }
]
}
},
"links": {
"self": "http://example.com/articles/1",
"--citation-report": {
"rel": ["http://example.com/notions/citation-reports"],
"href": "http://example.com/reports?type=citations&article_id=1",
"method": "get",
"type": "application/pdf",
"title": "A report on citations of the given article over the last 5 years.",
"meta": {
"source": "ISI Web of Science database in Computer Science"
}
}
}
},{
"type": "articles",
"id": "2",
"attributes": {
"title": "JSON API paints my bikeshed!",
"published": "false"
},
"relationships": {
"authors": {
"links": {
"self": "http://example.com/articles/2/links/authors",
"related": "http://example.com/articles/2/authors",
"--update": {
"rel": ["action"],
"href": "http://example.com/articles/2/links/authors",
"method": "patch"
},
"next": {
"href": "http://example.com/articles/2/authors?page=1&related_page_size=2",
"method": "get"
},
"last": "http://example.com/articles/2/authors?page=last&related_page_size=2"
},
"data": [
{ "type": "authors", "id": "5" },
{ "type": "authors", "id": "12" }
],
"meta": {
"pagination": {
"related_page_size": "2",
"related_page_count": "3",
"related_item_count": "5"
}
}
}
},
"links": {
"self": "http://example.com/articles/2",
"--update": {
"rel": ["action"],
"method": "patch",
"href": "http://example.com/articles/2"
},
"--delete": {
"rel": ["action"],
"method": "delete",
"href": "http://example.com/articles/2"
},
"--publish": {
"rel": ["action"],
"method": "post",
"href": "http://example.com/articles/2/publish"
},
"version-history": {
"method": "get",
"href": "http://example.com/articles/2/version-history"
},
"--version-drop-wip": {
"rel": ["action", "http://example.com/notions/version-drop-wip"],
"method": "post",
"href": "http://example.com/articles/2/version-drop-wip"
},
"type": "http://example.com/notions/articles"
}
},{
"type": "articles",
"id": "3",
"attributes": {
"title": "On Silver Bullets",
"published": "true"
},
"relationships": {
"authors": {
"links": {
"self": "http://example.com/articles/3/links/authors",
"related": "http://example.com/articles/3/authors",
"item": "http://example.com/authors/33"
},
"data": { "type": "authors", "id": "33" }
}
},
"links": {
"self": "http://example.com/articles/3"
}
}],
"links": {
"self": "http://example.com/articles?page=1&page_size=3&related_page_size=2",
"home": "http://example.com/start-api-exploration",
"copyright": {
"href": "http://example.com/copyright",
"type": "text/html"
},
"nexf": "http://example.com/articles?page=2&page_size=3&related_page_size=2",
"last": "http://example.com/articles?page=last&page_size=3&related_page_size=2"
},
"meta": {
"pagination": {
"page_size": "3",
"page_count": "15",
"item_count": "43"
}
}