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

Relationships between resources controlled by different APIs #675

Open
ethanresnick opened this Issue May 23, 2015 · 3 comments

Comments

Projects
None yet
4 participants
@ethanresnick
Copy link
Member

ethanresnick commented May 23, 2015

This proposal is for post 1.0. I don't think anything in it would require pre-1.0 changes, though let me know if I'm missing anything.

Sooner or later, we're probably going to want standard JSON API relationships to be able to connect together JSON API resources that are controlled by different APIs/organizations. This is a pretty big part of the web/hypermedia, and its presumably part of the reason the "relationships" bag was originally called "links".

I don't think enabling this functionality will be too hard (see my proposal below), nor do I think it will lead us back to the idea of merging relationships and links into one (which would be an annoying reversion). After all, a relationship will remain a specific kind of link, namely: one that points to a set of JSON API resources and that has a relationship endpoint. By contrast, an item in the generic "links" bag may not point to a JSON API endpoint (consider the profile links we're likely to add there) or may point to multiple distinct collections of resources (e.g. if we added a collection key that could take an array of entries). And, of course, none of the items in "links" will have relationship endpoints.

Ok, onto the proposal, which has three parts:

  1. We define a mapping from the "type" value used within an API to a URI, which can identify the type globally between APIs. This mapping might be: {scheme and authority of the API that owns the resource} + "/.well-known/jsonapi-types/" + {a percent-encoded version of the type string}. So, for example, if an API at example.com controls a resource that it serves with a "type" of "posts", the URI for that type is "http://example.com/.well-known/jsonapi-types/posts". (The /.well-known/ part comes from RFC 5785.)
  2. We define an optional "external" key at the top level of both resource objects and resource identifier objects. If present, the only valid value for this key is true. It denotes that the resource being represented or identified is external to (i.e. not controlled by) the API serving/recieving the resource object/identifier object.
  3. We add an optional "href" key to the resource identifier object. This key can be present regardless of whether the resource being identified is controlled by the API serving/receiving the resource identifier object. This will address @hhware's desire for a "target URI" but will also help with handling external resources, as discussed below.

The above parts all fit together as follows:

  1. When an external resource is added or removed from a relationship (at a relationship endpoint or in a standard PATCH/POST/DELETE), the resource identifier object facilitating this MUST contain "external": true, and the value of its "type" key must be the global, URI version of the external resource's type. When the relationship's data is later fetched (at a relationship endpoint or as part of a larger document), the (global) "type" and the "id" provided when the resource was added to the relationship, as well as the external boolean must be returned to the client in the resource identifier object.
  2. The optional "href" property SHOULD also be provided when adding the external resource to the relationship. If provided, this value SHOULD be stored and returned back to the client whenever the relationship's data is returned. This removes the client from needing to know how to construct a URI for the resource from its global type/id in order to read its contents.
  3. If an external resource is to be included in the response document via the include parameter, the server can simply return a resource object with three mandatory keys: the global type URI under the "type" key, the id in the "id" key, and the "external" key set to true. It can also, optionally, return a "self" link in the resource object that matches the href from the resource identifier object.
  4. For now, I'm imagining that, when external resources are included in a document as in (3), their resource objects don't contain any attributes or relationships. Likewise, any other resources that might be included based on the values of the relationships of the external resources would be ignored. That is, if the primary data has a users relationship that contains an some external resources, and the client asks for ?include=users.employer, the (potential) employer resources related to the external users would simply be ignored. However, in the future, this could be extended to allow the server to return a cached copy of the external resource's data, and perhaps use that data to compute nested includes, if it feels confident in the data's freshness.

Thoughts?

@hhware

This comment has been minimized.

Copy link
Contributor

hhware commented May 27, 2015

@ethanresnick, IMHO, an interesting beginning of what will hopefully be a great development!

@gkoehl

This comment has been minimized.

Copy link

gkoehl commented Jun 5, 2017

Is there any news on this topic? Has a solution been decided on? @ethanresnick @hhware

@niltz

This comment has been minimized.

Copy link

niltz commented Jan 18, 2019

I have an api that is split across multiple microservices, but there are some relationships between the resources in them. I've been struggling with how to define these relationships (since each microservice is essentially it's own api), and this feels like it would solve that problem. @ethanresnick, has any progress been made on this?

Here is an example of my current struggles:
https://discuss.jsonapi.org/t/resource-relationships-across-micro-services/1471

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment