Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow custom resource links #1678

Open
toedter opened this issue Nov 1, 2022 · 13 comments
Open

Allow custom resource links #1678

toedter opened this issue Nov 1, 2022 · 13 comments

Comments

@toedter
Copy link
Contributor

toedter commented Nov 1, 2022

Maybe I understand something wrong here, but the spec says:

Unless otherwise noted, objects defined by this specification or any applied extensions MUST NOT contain any additional members. Client and server implementations MUST ignore non-compliant members.

Furthermore the spec says:

The optional links member within each resource object contains links related to the resource.

If present, this links object MAY contain a self link that identifies the resource represented by the resource object.

I interpret this language so that the following JSON would NOT be compliant with the spec:

{
  "data": {
    "id": "4",
    "type": "movies",
    "attributes": {
      "title": "The Matrix"
    },
    "links": {
      "imdb": "https://www.imdb.com/title/tt0133093/"
    }
  }
}

If my interpretation is right, please allow custom links. Otherwise many REST hypermedia use cases could not be implemented in a spec-compliant way.

If my interpretation is wrong, please clarify the language of the spec by explicitly adding language to allow custom resource links.

Just a side note: If you validate the above JSON with https://www.jsonschemavalidator.net/ (and use the provided JSON:API schema), it says it is valid JSON:API.

@jelhan
Copy link
Contributor

jelhan commented Nov 1, 2022

Your interpretation of the specification is correct. If not stated explicitly implementations (and profiles) are not allowed to add additional members. This includes links objects.

The names are reserved for potential further usage by the specification itself. If implementations would be allowed to define additional members, adding new members to the specification would be a breaking change.

However this does not prevent usage of custom links. Extensions are allowed to define additional members. Including additional members of the links object.

@freddrake
Copy link
Contributor

It's also perfectly valid to have attributes whose values are URLs; URL values are not restricted to being placed in the links object.

{
  "data": {
    "id": "4",
    "type": "movies",
    "attributes": {
      "imdb": "https://www.imdb.com/title/tt0133093/",
      "title": "The Matrix"
    }
  }
}

@toedter
Copy link
Contributor Author

toedter commented Nov 1, 2022

Thx for the replies.

@jelhan Could an extension allow any custom links, without specifying them explicitely?

@freddrake While your suggestion would be syntactically compliant, I think this breaks the idea of REST (HATEOAS). IMHO links should be discoverable by knowing the media type structure, not by the implicite knowledge of the semantics of the data.

@jelhan
Copy link
Contributor

jelhan commented Nov 1, 2022

@jelhan Could an extension allow any custom links, without specifying them explicitely?

An extension can define any member name within their namespace. The namespace prevents collisions between members defined by an extension and potential additions to the base specification later. Please have a look at rules for extensions section for details.

While your suggestion would be syntactically compliant, I think this breaks the idea of REST (HATEOAS). IMHO links should be discoverable by knowing the media type structure, not by the implicite knowledge of the semantics of the data.

I don't see how your attempt to define any custom link fits with the purpose of HATEOAS. It is not enough that a link is discoverable. A consumer also needs to understand the meaning of that link. This is why the JSON:API specification carefully defines the meaning of the links.

I don't see much difference between { data: { attributes: { "imdb": "https://www.imdb.com/title/tt0133093/" } } and { data: { links: { "imdb": "https://www.imdb.com/title/tt0133093/" } } in that perspective. It might be easier to discover for a consumer that the value is an URL in the second example. But in both cases a consumer needs additional information to understand what that URL is about.

Please note that presenting data in links object would have similar drawbacks to using meta. Most importantly creating and updating these information is not defined by the specification. Attributes and relationships are meant to model data. I would highly recommend to use them.

@jelhan
Copy link
Contributor

jelhan commented Nov 1, 2022

Please see #1019 and #1656 for previous discussions on this topic.

@toedter
Copy link
Contributor Author

toedter commented Nov 1, 2022

@jelhan thx for the pointers.

@toedter
Copy link
Contributor Author

toedter commented Nov 2, 2022

Within the spec, the relationships structure is different compared with the links structure in terms of allowed members. I mean within a relationships object, every JSON key can be used as member, which makes total sense to me. So the following JSON is JSON:API compliant:

{
  "data": {
    "id": "4",
    "type": "movies",
    "attributes": {
      "title": "The Matrix"
    },
   "relationships": {
      "directors": {
        "links": {
          "related": "/directors/254"
        }
      }
    }
  }
}

@jelhan wrote:

I don't see how your attempt to define any custom link fits with the purpose of HATEOAS. It is not enough that a link is discoverable. A consumer also needs to understand the meaning of that link.

In the above example any client can figure out from the JSON:API structure that a movie has a relationship to another resource, and that the name of this relationship is directors. But the client also has to know the semantics of a director when following the related link within the relationship and then doing something useful with the response.

BTW, the background of this issue is the following. Many hypermedia media types have the approach that the name of a link (relation) can be chosen by the providing service, e.g. HAL, HAL-FORMS, Siren ...
Often services use content negotiation to provide different formats.

Many (or even most) of the links in above formats could probably be transformed into valid JSON:API relationships, but some probably cannot, because in a JSON:API relationship the related link ALWAYS has to link to a related REST resource. So @freddrake 's suggestion would be a valid way to express those links in JSON:API.

For me, the JSON:API spec standalone is fine, because most service developers could follow the following practice:

  1. Use relationships to express relations between rest resources
  2. When using links, stick to the allowed members
  3. Use custom attribute names that can contain an URI value otherwise

But if you have a generic programming model that provides a link abstraction, and you want to provide different media types as output, it is a bit harder to make the result JSON:API compliant.

@jelhan
Copy link
Contributor

jelhan commented Nov 16, 2022

Within the spec, the relationships structure is different compared with the links structure in terms of allowed members. I mean within a relationships object, every JSON key can be used as member

Both have very different purpose in the specification.

The relationships object represents links between different resource. In combination with the attributes object it is responsible for representing the data. The data model of an API is highly domain driven. It can not be standardized in a generic way. Therefore the spec only puts few constraints on it, e.g. that a relationship must not have the same name as an attribute.

The links object provides a client information how to construct additional requests. E.g. a self link of a JSON:API document provides a client all information needed to reload the document. A self link of a resource object provides needed information to reload, update or delete a specific document. A related link of a relationship object provides needed information to fetch the resource objects referenced by that relationship. Etc.

Further standardizing allowed members of a relationships object would very likely conflict with JSON:API's goal to be agnostic about the data model of an API. Less standardizing allowed members of a links object would prevent a client from use these links in a meaningful way.

But if you have a generic programming model that provides a link abstraction, and you want to provide different media types as output, it is a bit harder to make the result JSON:API compliant.

I would recommend to use an extension for these cases. An extension could not only define additional members but also define processing rules for them. Using an extension enables content negotiation.

@jelhan
Copy link
Contributor

jelhan commented Nov 16, 2022

BTW, the background of this issue is the following. Many hypermedia media types have the approach that the name of a link (relation) can be chosen by the providing service, e.g. HAL, HAL-FORMS, Siren ...

I'm not very familiar with all of them. But the _links object in HAL seems to have conceptually more in common with the relationships object of JSON:API spec than with the links object. The intent of _links in HAL is to expression relationships between resources. That's what the relationships object in JSON:API specification is about.

HAL does not seem to specify that a provided link must also return a HAL document. That seems to be the main difference compared to JSON:API specification.

@lode
Copy link
Contributor

lode commented Nov 16, 2022

I do see the value in custom resource links (I've actually used them for a long time, unaware of this limitation), for example for the html variant of the resource.

Using an extension to allow them feels like a big burden. Can't we use rules similar to query parameters? By only reserving a-z link names and allowing custom links if they use camelCame for example.

@freddrake
Copy link
Contributor

I do see the value in custom resource links (I've actually used them for a long time, unaware of this limitation), for example for the html variant of the resource.

Isn't this what content negotiation is for?

@jelhan
Copy link
Contributor

jelhan commented Nov 22, 2022

Using an extension to allow them feels like a big burden. Can't we use rules similar to query parameters? By only reserving a-z link names and allowing custom links if they use camelCame for example.

This is an interesting proposal. I feel we should discuss it with a broader perspective. Opened #1680 to do so.

@lode
Copy link
Contributor

lode commented Nov 23, 2022

Isn't this what content negotiation is for?

Do you mean this?

GET /articles/1 HTTP/1.1
Accept: text/html

Than it would only work for the same resource in another content type. E.g. only a single link per resource. That only allows html (or something alike) as extra link, not the freedom of multiple custom links.

Plus it would give a bit ugly urls for end users I'd say. Somewhere in the url (host/path) is probably an api keyword. You can redirect and such but I think that complicates the API server.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants