{"action":"created","issue":{"url":"https://api.github.com/repos/json-api/json-api/issues/622","labels_url":"https://api.github.com/repos/json-api/json-api/issues/622/labels{/name}","comments_url":"https://api.github.com/repos/json-api/json-api/issues/622/comments","events_url":"https://api.github.com/repos/json-api/json-api/issues/622/events","html_url":"https://github.com/json-api/json-api/issues/622","id":76812408,"number":622,"title":"Links, relationships, discoverable actions, mapping to RFC5988","user":{"login":"hhware","id":5066676,"avatar_url":"https://avatars.githubusercontent.com/u/5066676?v=3","gravatar_id":"","url":"https://api.github.com/users/hhware","html_url":"https://github.com/hhware","followers_url":"https://api.github.com/users/hhware/followers","following_url":"https://api.github.com/users/hhware/following{/other_user}","gists_url":"https://api.github.com/users/hhware/gists{/gist_id}","starred_url":"https://api.github.com/users/hhware/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hhware/subscriptions","organizations_url":"https://api.github.com/users/hhware/orgs","repos_url":"https://api.github.com/users/hhware/repos","events_url":"https://api.github.com/users/hhware/events{/privacy}","received_events_url":"https://api.github.com/users/hhware/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":8,"created_at":"2015-05-15T18:25:54Z","updated_at":"2015-05-19T07:42:29Z","closed_at":null,"body":"This proposal is a successor to #590. I would like to request to consider this for 1.0 because it won't make much sense afterwards.\r\n\r\n@ethanresnick has submitted #618 while I was thinking about this. Even though the content of #618 is a part of what is proposed here, the scope of this issue is different from #618, so I am not hijacking #618...\r\n\r\n##### Motivation\r\n\r\n1. Five HTTP verbs are not enough to describe all possible actions available for a resource in a complex API. The solution is to add custom endpoints. There should be a way to associate those custom endpoints with the primary endpoint of the resource.\r\n2. It would be great if JSON API allowed to build HATEOAS-compatible APIs. This requires ability to dynamically determine which actions can be taken on a given resource and its relationships, as well as what are the starting points to explore the API without much prior knowledge about it. It is often important for the clients to save trips to the server, so such information should be returned as part of a response, not on a separate endpoint.\r\n3. Bring consistency to top-level and resource-level `links` and to relationships. A lot has been said on this. One thing I am trying to solve here is that each relationship provides multiple links, whereas members of top-level `links` and resource-level `self` are singular links, so they are not similar at all, one is a actually a component of another. Another aspect of it is making all `links` have the same structure, which can also be considered as a step towards the concept of \"`links` can appear anywhere\" (https://github.com/json-api/json-api/pull/588#issuecomment-98261811).\r\n4. It would be useful to allow implementations to provide various links relevant to the given resource or response object, whether \"verbs\" or \"nouns\", whether representing actions on the endpoints of resource/its relationships/custom endpoints, or different aspects/views/versions/etc of the given resource, or anything else related to the given resource. Currently, only a very limited set of links is allowed (relationships, pagination, self), and `meta` is the only option for those who need more. It is workable, but it will drive fragmentation in the emerging JSON API ecosystem.\r\n5. Provide an implementation of [linking in JSON](https://www.mnot.net/blog/2011/11/25/linking_in_json): allow association of [RFC5988 Link Relations](https://www.iana.org/assignments/link-relations/link-relations.xml) to *all* links. This must be optional in order not to affect adoption of the spec.\r\n6. Make links extendable: they are currently defined as strings, which does not allow to add any customization/context to them, either in basic spec in the future or via extensions today.\r\n7. While achieving all this, the subset of the spec which defines minimally required data structures should not become much more complicated than it currently is, so that adoption is not affected.\r\n\r\n##### Proposed changes\r\n\r\n1. ~~Move relationships under resource-level key `relationships`, keeping `self` under resource-level key `links`. Relationships consist of *relationship objects* (formerly link objects) keyed with relationship name (#618).~~ *[This is implemented in #618, #625]*\r\n1. ~~Relationship objects consist of 3 members: `links`, `linkage`, `meta` (at least one is required).~~ *[This is implemented in #618, #625]*\r\n1. ~~Member `links` has identical definition whether it occurs on top, resource, or relationship level.~~ *[This is implemented in #618, #625]*\r\n1. Two forms of member `links` are possible: full (always available) or simplified (only available in special cases).\r\n1. Full form of member `links`: an array, each element of it is a link object.\r\n1. Define *link object* in the spec as an object having the following members: \r\n  1. `name`: required, self-explanatory,\r\n  1. `href`: required, self-explanatory, \r\n  1. `class`: required, classification of link type. It is a CSV-string with elements from the following set: `general,pagination,action,version,hierarchy,custom` (this set is obviously meant to be extendable in the future). Each link may have multiple classes. Elements of the set mean the following:\r\n    1. `general` (could not find a good name for it): for generally applicable links (possible values: `self,related,type,copyright,help`),\r\n    1. `pagination`: includes pagination members in top-level and relationship-level `links` (possible values: `prev,next,first,last`),\r\n    1. `action`: actions on the given resource or relationships, available to the given user at the given moment of time, either at the resource's own endpoint or at some custom endpoint (possible values: `create,update,delete,explore`; general `self` substitutes the action of reading the current resource/relationship -- we might want to try to eliminate this inconsistency),\r\n    1. `version`: links pointing at other versions of the given resource or providing versioning information about it (possible values: `version-history,working-copy,successor-version,predecessor-version,latest-version,latest-published-version`, see RFC5988),\r\n    1. `hierarchy`: links for handling hierarchical content (possible values: `up`, see RFC5988),\r\n    1. `custom`: all other (yet unclassified) links.\r\n  1. `method`: optional, contains HTTP verb,\r\n  1. `rfc5988`: optional, allows to add RFC5988 Link Relations. If defined, it should contain an object consisting of members keyed by names of `link-param`s from RFC5988 or its extensions. `URI-Reference` is considered to be set in `href`.\r\n1. Attributes share namespace with relationships but not with links.\r\n1. In each given member `links`, each link object is identified by a pair `name` and `class` (canonicalized, e.g. with elements sorted alphabetically), so such pairs must be unique. (Alternatively, `class` can be defined as array, e.g., `\"class\":[\"pagination\",\"custom\"]`: alphabetic reordering and serialization are needed for use as key.)\r\n1. The spec should be able to reserve names of links of standard classes in order to have freedom for future extensions, but at the same time it would be useful if the spec allowed implementations to extend standard classes or define completely arbitrary links. To achieve that, the spec reserves possible ranges of values of `name` for link objects classified with one of `general,pagination,action,version,hierarchy`. Link with `class` ~~having any other value or any combination of values~~ *including any other value* can have unrestricted `name`. E.g., if a link has `\"class\":\"pagination\"`, it could only have `name` as one of `prev,next,first,last`, but if it has `\"class\":\"pagination,custom\"` or just `\"class\":\"custom\"`, its `name` can be anything. All reserved values of `name` defined by spec must be unique, regardless of class. Proposed values per class to be defined by the spec are listed above in parentheses.\r\n1. If member `links` only has link objects classified with single class, none of which is `custom`, and none of link objects has optional members, then such `links` can be returned by the server in simplified form: an object where values of `href` are keyed by values of `name` (`class` can be derived from `name` because `name` is unique for non-custom link classes). *As of #625,* the simplified form is just ~~a tiny bit more complex than~~ the current version of the spec. Perhaps the full form can be described in a separate section of the base spec, not in the primary text flow of the spec. On another hand, support for full form could be an official extension, but IMHO it should be a part of the base spec.\r\n1. In principle, link objects defined this way can be used anywhere in the response. E.g., the implementation can consider returning all attributes which contain URLs as link objects in full form.\r\n1. Top-level link with `\"class\":\"action\"` and name `explore` could be used to communicate starting point for browsing a HATEOAS-compliant API.\r\n\r\n##### Example\r\n\r\nIllustrates 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 actions (report for article with ID=1). \r\n\r\nI am not suggesting to allow mix of different link forms in one response, it is here only to shorten the example.\r\n\r\n```http\r\nGET /articles?page=1&page_size=3&related_page_size=2\r\n\r\n{\r\n  \"data\": [{\r\n    \"type\": \"articles\",\r\n    \"id\": \"1\",\r\n    \"attributes\": {\r\n      \"title\": \"JSON-LD paints my bikeshed!\",\r\n      \"published\": \"true\"\r\n    },\r\n    \"relationships\": {\r\n      \"authors\": {\r\n        \"links\": {\r\n          \"self\": \"http://example.com/articles/1/links/authors\",\r\n          \"related\": \"http://example.com/articles/1/authors\",\r\n        },\r\n        \"linkage\": [\r\n          { \"type\": \"authors\", \"id\": \"3\" },\r\n          { \"type\": \"authors\", \"id\": \"4\" }\r\n        ]\r\n      }\r\n    },\r\n    \"links\": [{\r\n      \"name\": \"self\",\r\n      \"class\": \"general\",\r\n      \"href\": \"http://example.com/articles/1\"\r\n    },{\r\n      \"name\": \"citation-report\",\r\n      \"class\": \"custom\",\r\n      \"method\": \"get\",\r\n      \"href\": \"http://example.com/reports?type=citations&article_id=1\",\r\n      \"rfc5988\": {\r\n        \"rel\": \"http://example.com/notions/citation-reports\",\r\n        \"type\": \"application/pdf\",\r\n        \"title\": \"A report on citations of the given article over the last 5 years. \r\nData is derived from ISI Web of Science database in Computer Science.\"\r\n      }\r\n    }]\r\n  },{\r\n    \"type\": \"articles\",\r\n    \"id\": \"2\",\r\n    \"attributes\": {\r\n      \"title\": \"JSON API paints my bikeshed!\",\r\n      \"published\": \"false\"\r\n    },\r\n    \"relationships\": {\r\n      \"authors\": {\r\n        \"links\": [{\r\n          \"name\": \"self\",\r\n          \"class\": \"general\",\r\n          \"href\": \"http://example.com/articles/2/links/authors\",\r\n          \"rfc5988\": {\r\n            \"rel\": \"self\"\r\n          }\r\n        },{\r\n          \"name\": \"related\",\r\n          \"class\": \"general\",\r\n          \"href\": \"http://example.com/articles/2/authors\",\r\n          \"method\": \"get\",\r\n          \"rfc5988\": {\r\n            \"rel\": \"related author\"\r\n          }\r\n        },{\r\n          \"name\": \"update\",\r\n          \"class\": \"action\",\r\n          \"href\": \"http://example.com/articles/2/links/authors\",\r\n          \"method\": \"patch\"\r\n        },{\r\n          \"name\": \"next\",\r\n          \"class\": \"pagination\",\r\n          \"href\": \"http://example.com/articles/2/authors?page=1&related_page_size=2\",\r\n          \"method\": \"get\",\r\n          \"rfc5988\": {\r\n            \"rel\": \"related author next\"\r\n          }\r\n        },{\r\n          \"name\": \"last\",\r\n          \"class\": \"pagination\",\r\n          \"href\": \"http://example.com/articles/2/authors?page=last&related_page_size=2\",\r\n          \"method\": \"get\",\r\n          \"rfc5988\": {\r\n            \"rel\": \"related author last\"\r\n          }\r\n        }],\r\n        \"linkage\": [\r\n          { \"type\": \"authors\", \"id\": \"5\" },\r\n          { \"type\": \"authors\", \"id\": \"12\" }\r\n        ],\r\n        \"meta\": {\r\n          \"pagination\": {\r\n            \"related_page_size\": \"2\",\r\n            \"related_page_count\": \"3\",\r\n            \"related_item_count\": \"5\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    \"links\": [{\r\n      \"name\": \"self\",\r\n      \"class\": \"general\",\r\n      \"href\": \"http://example.com/articles/2\"\r\n    },{\r\n      \"name\": \"update\",\r\n      \"class\": \"action\",\r\n      \"method\": \"patch\",\r\n      \"href\": \"http://example.com/articles/2\"\r\n    },{\r\n      \"name\": \"delete\",\r\n      \"class\": \"action\",\r\n      \"method\": \"delete\",\r\n      \"href\": \"http://example.com/articles/2\"\r\n    },{\r\n      \"name\": \"publish\",\r\n      \"class\": \"action,custom\",\r\n      \"method\": \"post\",\r\n      \"href\": \"http://example.com/articles/2/publish\"\r\n    },{\r\n      \"name\": \"version-history\",\r\n      \"class\": \"version\",\r\n      \"method\": \"get\",\r\n      \"href\": \"http://example.com/articles/2/version-history\",\r\n      \"rfc5988\": {\r\n        \"rel\": \"version-history\"\r\n      }\r\n    },{\r\n      \"name\": \"version-drop-wip\",\r\n      \"class\": \"version,action,custom\",\r\n      \"method\": \"post\",\r\n      \"href\": \"http://example.com/articles/2/version-drop-wip\",\r\n      \"rfc5988\": {\r\n        \"rel\": \"http://example.com/notions/version-drop-wip\"\r\n      }\r\n    },{\r\n      \"name\": \"type\",\r\n      \"class\": \"general\",\r\n      \"href\": \"http://example.com/notions/articles\",\r\n      \"rfc5988\": {\r\n        \"rel\": \"type\"\r\n      }\r\n    }]\r\n  },{\r\n    \"type\": \"articles\",\r\n    \"id\": \"3\",\r\n    \"attributes\": {\r\n      \"title\": \"On Silver Bullets\",\r\n      \"published\": \"true\"\r\n    },\r\n    \"relationships\": {\r\n      \"authors\": {\r\n        \"links\": {\r\n          \"self\": \"http://example.com/articles/3/links/authors\",\r\n          \"related\": \"http://example.com/articles/3/authors\"\r\n        },\r\n        \"linkage\": { \"type\": \"authors\", \"id\": \"33\" }\r\n      }\r\n    },\r\n    \"links\": {\r\n      \"self\": \"http://example.com/articles/3\"\r\n    }\r\n  }],\r\n  \"links\": [{\r\n    \"name\": \"self\",\r\n    \"class\": \"general\",\r\n    \"href\": \"http://example.com/articles?page=1&page_size=3&related_page_size=2\"\r\n  },{\r\n    \"name\": \"explore\",\r\n    \"class\": \"action\",\r\n    \"href\": \"http://example.com/start-api-exploration\"\r\n  },{\r\n    \"name\": \"copyright\",\r\n    \"class\": \"general\",\r\n    \"href\": \"http://example.com/copyright\",\r\n    \"rfc5988\": {\r\n      \"rel\": \"copyright\",\r\n      \"type\": \"text/html\"\r\n    }\r\n  },{\r\n    \"name\": \"next\",\r\n    \"class\": \"pagination\",\r\n    \"href\": \"http://example.com/articles?page=2&page_size=3&related_page_size=2\",\r\n    \"rfc5988\": {\r\n      \"rel\": \"next\"\r\n    }\r\n  },{\r\n    \"name\": \"last\",\r\n    \"class\": \"pagination\",\r\n    \"href\": \"http://example.com/articles?page=last&page_size=3&related_page_size=2\",\r\n    \"rfc5988\": {\r\n      \"rel\": \"last\"\r\n    }\r\n  }],\r\n  \"meta\": {\r\n    \"pagination\": {\r\n      \"page_size\": \"3\",\r\n      \"page_count\": \"15\",\r\n      \"item_count\": \"43\"\r\n  }\r\n}\r\n```\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/json-api/json-api/issues/comments/103382607","html_url":"https://github.com/json-api/json-api/issues/622#issuecomment-103382607","issue_url":"https://api.github.com/repos/json-api/json-api/issues/622","id":103382607,"user":{"login":"ethanresnick","id":471894,"avatar_url":"https://avatars.githubusercontent.com/u/471894?v=3","gravatar_id":"","url":"https://api.github.com/users/ethanresnick","html_url":"https://github.com/ethanresnick","followers_url":"https://api.github.com/users/ethanresnick/followers","following_url":"https://api.github.com/users/ethanresnick/following{/other_user}","gists_url":"https://api.github.com/users/ethanresnick/gists{/gist_id}","starred_url":"https://api.github.com/users/ethanresnick/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ethanresnick/subscriptions","organizations_url":"https://api.github.com/users/ethanresnick/orgs","repos_url":"https://api.github.com/users/ethanresnick/repos","events_url":"https://api.github.com/users/ethanresnick/events{/privacy}","received_events_url":"https://api.github.com/users/ethanresnick/received_events","type":"User","site_admin":false},"created_at":"2015-05-19T07:42:29Z","updated_at":"2015-05-19T07:42:29Z","body":"> I doubt many people would actually want to use URLs for names because they are not very readable and might hinder communication\r\n\r\nI agree that URIs as keys are ugly, but prefixes do ameliorate this to a large extent. And we can get even more clever than my example earlier. For example, we could define the `api` prefix as automatically mapping to the `{current host}/rels/`. So an api at `http://example.com/v2` could return a resource-level `links` that looks like this:\r\n\r\n```\r\n\"links\": {\r\n  \"api:publish\": \"http://example.com/v2/articles/2/publish\"\r\n}\r\n```\r\nAnd `api:publish` would be mapped to the url \"http://example.com/rels/publish\" without the user having to define `http://example.com/rels/` as the value of a prefix anywhere. (And this works even if the user doesn't ultimately bother to put anything at the `http://example.com/rels/publish` URI.)\r\n\r\nThe above still isn't as clean as a simple `\"publish\"` key, it's true. But, if we have a `name`, we still have to put the registered relation name or (something that maps to) a relation URI _somewhere_ for us to have RFC 5988 compliance. And, to me, whatever ugliness is left in the `api:publish` key is preferable to having both a `name` and a relation mapping, which seems redundant. I don't like the idea of links with keys like `\"name\": \"first\", \"rel\": \"first\"`, `\"name\": \"self\", \"rel\": \"self\"`, etc.\r\n&nbsp;\r\n\r\n> You suggest to key them by name, but what if a particular API defines a custom link name, and JSON API decides to reserve it a few years later? \r\n\r\nFuture JSON-API keys might be extended relations, in which case they'd live at `http://jsonapi.org/rels/whatever` and couldn't conflict with user-defined keys (which live on other domains). The only possibility for conflict, therefore, would be if JSON API later defines a more-specific meaning for an existing registered link relation, and the user was already using that relation in their API with the generic meaning (but not the json-api–specific specialized meaning). But that possibility could be prevented by the right reasoning in #614.\r\n\r\n> The main difference between our proposals at this point is how links are identified... My proposal is closer to how `included` is structured: an array with a bunch of heterogeneous objects.\r\n&nbsp;\r\n\r\nYup! And you're right that your structure is closer to `included`. But it's also less similar to the current/\"simplified\" form of a `links` object that's in the base spec. I'd rather not sometimes have `links` as an object and sometimes have it be an array. Instead, under my proposal it's always an object; the relation is always the key; and the value is made up of a set of \"building blocks\", namely:\r\n\r\n1. A \"link target\" object, which must contain an `\"href\"` key and can contain other keys, like `method`, `content-type`, etc. A `class` key could go here too; I'm still not 100% sold on it (namely, because I worry we'd get the taxonomy wrong), but it would fit into the structure perfectly.\r\n2. A \"link target\" object can be expressed more compactly as just a string, which equates to an object with just an href member.\r\n3. And the value of the link is a single \"link target\" object/string, or an array of such objects."}}