Skip to content

Links: general approach, discoverable actions, mapping to RFC5988 (take II) #684

@hhware

Description

@hhware

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
  1. Each member of a links object can be either a link object or an array of link objects.
  2. Each member of a links object can be keyed with either of
    1. RFC5988 registered relation type. All current link names are of this category.
    2. RFC5988 extension relation type, i.e. an URI.
    3. A custom link name: a string prefixed with something, I suggest double dash (--) for this purpose. If defined this way, it
      1. follows the style of existing registered relation names,
      2. does not conflict with reg-rel-type naming rule from RFC5988,
      3. keeps these links easily identifiable as custom links,
      4. keeps them as readable as natural language words/phrases.
  3. In the cases (2)(i) and (2)(ii), the list of possible members of links object is
    1. href: required, self-explanatory,
    2. method: optional, contains HTTP verb,
    3. members keyed by names of link-params from RFC5988 or its extensions: optional (alternatively, list them under rfc5899 member, as proposed in Links, relationships, discoverable actions, mapping to RFC5988 #622).
  4. In the case (2)(iii), an additional member of link object can be provided by the server:
    1. rel: array, optional, contains RFC5988 relation names, either registered or extensions.
  5. (2)(i) and (2)(ii) cover the cases when the representation of links in a given API is driven by semantics of RFC5988.
  6. (2)(iii) covers the following use cases, which cannot be easily covered by (2)(i) and (2)(ii):
    1. 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,
    2. 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,
    3. when API prefers to identify links by distinct name rather than relation name, and to apply relation name to a group of links,
    4. 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.
  7. Comparing to Links, relationships, discoverable actions, mapping to RFC5988 #622,
    1. 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,
    2. equivalent for name is only available in case (2)(iii), as it is has no match in RFC5988.
  8. 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.
  9. Dedicated issue: Suggestion: "target", a reserved member of relationship linkage  #477. For relationship-level links, define a member item (another name could be used, such as target, related-target, etc., but it should be registered with IANA) to list target URLs of the relationship.
  10. 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 class explore in Links, relationships, discoverable actions, mapping to RFC5988 #622).
  11. 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"
  }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy