JSON API for non-resource responses


#1

I see similar question already here (Json API response format for non-resource data, like oAuth token), but it doesn’t have clear answer for me.

I already asked in stackoverflow: https://stackoverflow.com/questions/49754327/json-api-for-non-resource-responses?noredirect=1#comment86523631_49754327

Currently, I’m working on new product and making REST API for both - public and internal needs. I started with {json:api} specification and I was pretty happy with it until I faced some questions I cannot find answers to.

According to JSON API specification, every resource MUST contain id.

http://jsonapi.org/format/

Every resource object MUST contain an id member and a type member. The values of the id and type members MUST be strings.

And that’s fine in many cases but not all.

Most of our endpoints are about “resources”

If I ask for a “things” collection (http://example.com/things)

{
  "data": [{
    "type": "things",
    "id": "1",
    "attributes": {
      "title": "first"
    },
    "links": {
      "self": "http://example.com/things/1"
    }
  }, {
    "type": "things",
    "id": "1",
    "attributes": {
      "title": "second"
    },
    "links": {
      "self": "http://example.com/things/2"
    }
  }]
}

If I ask for a single “things” resource (http://example.com/things/1)

{
  "data": {
    "type": "things",
    "id": "1",
    "attributes": {
      "title": "first"
    },
    "links": {
      "self": "http://example.com/things/1"
    }
  }
}

But what to do with endpoints which are not about resources and does not have ID?

For example, in our application, there is an endpoint http://example.com/stats which should return stats of current logged in user. Like

{
  "active_things": 23,
  "last_login": "2017"
}

There is no id for this “resource” (it’s not actually a resource, is it?). Backend just collects some “stats” for logged in user and returns an object of stats. There many endpoints like this in this application, for example, we have Notification center page where the user can change email addresses for different notifications.

So frontend app (single-page-app) first has to get current values and it sends the request to GET http://example.com/notification-settings.

{
  "notifications_about_new_thing": "arunas@example.com",
  "notification_about_other_thing": "arunas@example.com"
}

And there are many more endpoints like this. The problem is - how to return these responses in JSONAPI format? There is no ID in these endpoints.

And the biggest question is - why nobody else is facing this issue (at least I cannot find any discussion about this)? :smiley: All APIs I ever made has some endpoints which don’t have “id”.

I have two ideas, first is to fake id, like "id": "doesnt_matter", the second - do not use json-api for these endpoints. But I don’t like both of them.


#2

Welcome!

When dealing with an issue of an ‘endpoint’ which isn’t a resource, my first response is and the most common solution is to answer the question why? Taking into account the rest of the semantics you explained leads me to believe the solution is to make the /stats endpoint a sub resource which you don’t include unless explicitly queried with /user/{id}?fields[user]=stats, in this case all you are looking at is additional specific information regarding the user resource.

Now, looking at your notification scenario I generally tend to ask myself early on in the resource design process if there is a way to think about this request like filling out forms in the DMV.

To answer your “why is nobody else facing this issue…?” question, it is because you are implementing an RPC over HTTP web api, and your search keywords aren’t likely to find details on resolving the design issue. You don’t have an issue with representing a resource in {json:api}, your API itself is not resource oriented in it’s design. Your API is highly coupled to the needs of the application it is serving, which isn’t necessarily wrong generally but is within the context of a REST API. REST APIs are not databases or document stores over HTTP, they should be abstracted and decoupled from the clients they serve and based upon the domain of the industry or field you are addressing.

A simple JSON representation for the /notifications resource could be something like this:

[
  {
    "notification_id":1,
    "notification_name": "notifications_about_new_thing",
    "notification_channel": "email",
    "notification_target": "arunas@example.com"
  },
  {
    "notification_id":2,
    "notification_name": "notification_about_other_thing",
    "notification_channel": "email",
    "notification_target": "arunas@example.com"
  }
]

The benefit of this approach, is it is now designed well to have all notification channels independently updated because it now has a unique identifier. Further, I would ensure there is a relationship between the notifications and the user resource which would allow you to filter for the range of user notifications get /notifications?filter[user]={userId} returning the appropriate {json:api} array representation.

I touch on these and other topics on my blog if you would like to read some more details hypermedia api design guidelines and crud vs hypermedia.

I hope this helps!