Relationship URIs for special "helper" URIs

I’m putting together a new API using JSONAPI as the basis. As part of this, taking a leaf from Google, I’m implementing a special URI endpoint for retrieving the “Current User”. This will work by retrieving “/api/users/me” instead of “/api/users/123”, and will always return the user details of the user for whom you have an access token.

This works really well, until I get to relationships. Imagine I have a relationship from the User to their Posts. This would normally look like:

{
    "type": "users",
    "id": "123",
    "attributes": {},
    "links": {
        "self": "/api/users/123"
    },
    "relationships": {
        "posts": {
            "links": {
                "related": "/api/users/123/posts"
            },
            "data": []
        }
    }
}

However, if I’m retrieving the Current User on the “/api/users/me” endpoint, what URI should I use for the relationships.posts.links.related value? Should it be /api/users/123/posts or /api/users/me/posts?

My immediate reaction is to cringe, and then the next thought is the absolute URI should be used as it isn’t ambiguous.

I can’t think of anywhere in the specification which specifically talks against this pattern, but it seems to go against everything I know about hypermedia. As /api/users/me will never be consistent across users, as it is utilizing request state to respond.

I think a much MUCH better solution would be to implement this as a filter parameter, and broadcast this as a link of some sort, something akin to:

/api/users?filter[id=self/me/..]

Implemented however your filtering strategy allows, this wouldn’t violate the intent of the spec nor the definition itself.

I still find this solution less than ideal, as the client should get this information at the same time it receives it’s authentication token. The responsibility to keep track of this information should fall on the client, if it’s being stored in a cookie or localstorage it should stay there for access.

Anyway, I’m done bikeshedding on this for now. Good luck.

Why not use something like /api/me or /api/profile instead of nesting it underneath the users resource? That allows the /api/profile endpoint to be polymorphic, if you ever happen to need to authenticate as something other than a user e.g. it returns the current bearer—be it an admin, team, etc.

And regarding your relationship question: I would always return canonical URIs i.e. with their ID.

1 Like

Resources are not objects (despite the name in the specification), a resourceType field or better yet hypermedia could handle type extension or bespoke inheritance. There is no guarantee of hierarchical or multiple inheritance polymorphism, designing off of the presumption it is could be really dangerous by including extremely subtle bugs.

The best proposal after finishing my cringing would be implement hypermedia, but since that would likely just confuse the situation more without much benefit you should go with the filter option, as this is the intended format of the spec for this type of functionality.

I have to retract this portion, since I can’t edit my post I’ll just add here. I had forgotten json api doesn’t support templated links, so the only option is to utilize filters.

Cheers!

I never said resources were objects, I was only meaning that the endpoint could respond with different resource types depending on who the current authenticated token bearer is e.g. a user, an admin, a CI token, etc., making the endpoint polymorphic (not strictly related to the OOP term). As far as I know, the spec doesn’t say endpoints are required to respond with only a single resource type.

If you use something like /api/users/me, you have to add the same endpoint for other resources (/api/admin/me, etc.) if you want to respond with whoever is authenticated. This is of course an example assuming the API allows different types of resources to authenticate i.e. not only users.

Either way, I think it’s a clean solution.

Ok, going with your definition of the term in this context I understand your point. However the concept of adding a stateful /me endpoint is not a good decision. Is it convenient? Yes. Does it violate the spec? Yes. Does it violate very beneficial Restful constraints? Yes.

The specification constraints don’t exist simply to exist, but to create a consistent design with particular benefits which is accepted across all clients that ‘speak’ json:api. You either follow the specification to get the benefits, or you aren’t building a json:api message. Either option is OK if you understand the consequences, but what ISN’T ok is to violate json:api spec and still call it so.

Bike shedding over RMM2 semantics is not particularly beneficial, and certainly goes against the intent of json:api.

1 Like

So there’s no need to get confrontational. Not everybody knows the spec as well as you do, so let’s educate without coming off as condecending. Can you point me to a location in the spec that says endpoints that respond with multiple resource types isn’t allowed? Maybe I missed it.

I was not being confrontational or condescending, the suggestion violates the constraints of HTTP URI specification, and the intent of json:api to create a uniform interface URI structure. By adding a /me appendage you no longer can use generic json:api clients effectively without constant maintenance.

@Sazzer To answer your question, in the “document links” the self link should contain the URL that generated the document - in this case /api/users/current-user. The individual resource relationships and links should be constructed in the context of that individual resource - in this case /api/users/123.

Here is another simple example: /api/locations/{locationId}/temperatures. Hitting this URL would get the history of temperatures at this location. Each temperature resource has some unique identifier, like an incrementing integer or some timestamp, etc. You could then have something like /api/locations/{locationId}/temperatures/current to get the current temperature at this moment of time. As you keep hitting that URL the result is changing, etc. Again, the document links “self” contains /api/locations/{locationId}/temperatures/current but the actual resource links and relationships hypermedia would be built for that moment of time when the temperature was taken…

Hope that helps…

1 Like

@Sazzer Also I would have the “current user” off of what I am calling the “API entry point document”. Although JSON API does not explicitly standardize this, it is the initial document that a client application uses to discover all the available resource or resource collections. The URL to this “API entry point” is the only URL any client application needs to know as further hypermedia is discovered at runtime, following “rels”, etc.

Here is a condensed version of the hypermedia API entry point I created for “our” hypermedia API where the “latest” box-office and ticket-trends return a single resource document that represent the latest at that moment of time :

{
  "links": {
    "up": "https://dev.api.movietickets.com:443/v2",
    "self": "https://dev.api.movietickets.com:443/v2/en-us"
  },
  "data": {
    "type": "api-entry-point",
    "attributes": {
      "message": "Entry point into the MovieTickets Hypermedia API.",
      "api-version": {
        "major-number": 2,
        "minor-number": 1,
        "build-number": 246
      },
      "website": "http://www.movietickets.com",
      "mobile-website": "http://mobile.movietickets.com"
    },
    "links": {
      "self": "https://dev.api.movietickets.com:443/v2/en-us",

      "box-office": "https://dev.api.movietickets.com:443/v2/en-us/box-offices",
      "box-office-latest": "https://dev.api.movietickets.com:443/v2/en-us/box-offices/latest",
      ...
      ...
      ...
      "ticketing-trends": "https://dev.api.movietickets.com:443/v2/en-us/ticketing-trends",
      "ticketing-trends-latest": "https://dev.api.movietickets.com:443/v2/en-us/ticketing-trends/latest",
      ...
      ...
      ...
    }
  }
}

This is a tangent that maybe deserves its own thread, but I’m glad you brought up the notion of an entry point document/resource. I wish this were part of the JSON API standard, because everyone’s going to do it differently… In the API I’m building, my root resource has relationships that link to the various index resources, rather than putting them in the resource links object as you do. My thinking is that then they can be included, in case a consumer wants to fetch multiple sets of resources at once. (I haven’t implemented this yet because I think this requires supporting pagination of included relationships, which the library I’m using, JSONAPI::Resources, doesn’t currently support out of the box.)

A few other comments about your approach:

  1. Shouldn’t your resource object include an “id” member, even if it’s a singleton resource? The standard seems pretty clear that an “id” member is required for every resource object. In your case, it seems like the id could just be “en-us”, given the distinction between the self link and the “up” link (though I’m curious about what resource lives at the “up” link…).
  2. Why are “website” and “mobile-website” represented as attributes instead of links? Perhaps your convention is that all links that appear in a “links” object are links to JSON API resources? The standard doesn’t say that anywhere, but I could see how that might be useful. Conversely, I’ve been putting everything that is a URL into the “links” objects, with the thought that a hypermedia client is going to know how to traverse those links, but wouldn’t necessarily be able to traverse an attribute that happens to have a URL value. (In this case the client is probably not going to want to consume the “website” link since it’s probably HTML only rather than an API, but I’m thinking of things like links to external APIs, or ancillary resources like images.)
  3. I would have expected “message” and “api-version” to be part of a “meta” object rather than “attributes”, because they’re not really resource data that e.g. a user might want to update, but that’s probably moot and just a matter of taste.

@dougo Doug thanks for the comments. I think you are right in that this important topic of an “API Entry Point Document” should be in it’s own thread. I will create another thread with this topic and use my own entry point document as a starting point to see what others think, what they have done, etc. I will even answer your “up” question and I hope you ask the same questions/comments you have here to get the thread started…