How to use sparse field sets for meta, links and relationships

For the given resource:

{
  "links": {
    "self": "http://example.com/articles",
    "next": "http://example.com/articles?page[offset]=2",
    "last": "http://example.com/articles?page[offset]=10"
  },
  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON API paints my bikeshed!",
      "rating": 5,
      "createdAt": "2018-01-05 10:55"
    },
    "meta": {
         "numberOfViews": 25,
         "copyright": "2018 (c) All rights Reserved"
     },
    "relationships": {
      "author": {
        "links": {
          "self": "http://example.com/articles/1/relationships/author",
          "related": "http://example.com/articles/1/author"
        },
        "data": { "type": "people", "id": "9" }
      },
      "publisher": {
        "links": {
          "self": "http://example.com/articles/1/relationships/publisher",
          "related": "http://example.com/articles/1/publisher"
        },
        "data": { "type": "publishers", "id": "10" }
       }
      "comments": {
        "links": {
          "self": "http://example.com/articles/1/relationships/comments",
          "related": "http://example.com/articles/1/comments"
        },
        "data": [
          { "type": "comments", "id": "5" },
          { "type": "comments", "id": "12" }
        ]
      }
    },
    "links": {
      "self": "http://example.com/articles/1"
    }
  }],
  "included": [{
    "type": "people",
    "id": "9",
    "attributes": {
      "first-name": "Dan",
      "last-name": "Gebhardt",
      "twitter": "dgeb"
    },
    "links": {
      "self": "http://example.com/people/9"
    }
  }, {
    "type": "comments",
    "id": "5",
    "attributes": {
      "body": "First!"
    },
    "relationships": {
      "author": {
        "data": { "type": "people", "id": "2" }
      }
    },
    "links": {
      "self": "http://example.com/comments/5"
    }
  }]
}

We can use sparse fieldsets to choose the fields attributes we want returned, like so:

fields[offer]=title,rating

However, how can we use sparse fieldsets to choose other data, such as:

  • meta data. e.g. only numberOfViews
  • relationships e.g. perhaps you only want to show the author, but not the comments. You might also not want to include the links.
  • links at any level. e.g. you might not want to include the links at the root, or relationship links.
  • type or id

I have come up with 3 solutions:

Solution 1
We can extend the spec to accept more options like so:

rootFields[offer]=id
fields[offer]=title,rating
metaFields[offer]=numberOfViews   
relationshipFields[offer]=author,publishers.data.id
links[offer]=                       // Doesn't return anything for any offer links

This also has the advantage of using default values if the parameter is missing. For example, not adding rootFields[offer] will mean type and id is always included.

Solution 2
Change the spec slightly like so:

fields[offer][root]=id
fields[offer][attributes]=title,rating
fields[offer][meta]=numberOfViews   
fields[offer][relationships]=author,publishers.data.id
fields[offer][links]=                // Doesn't return anything for any offer links

This also has the advantage of using default values if the parameter is missing. For example, not adding fields[offer][root] will mean type and id is always included.

But I think this may be a breaking change from the the specification, which I don’t really want to do.

Solution 3
Allow values in fields that are not attributes. e.g.

fields[offer]=root.id,title,rating,numberOfViews,relationships.author,relationships.publishers.data.id

Unfortunately, this means I cannot use default values.

Is there a better solution (preferably something that is compliant with the spec)?
Does the above 2 solutions seem suitable. If so which is better?

Type and id are required to identify the resource, it makes no sense to skip them. If you only want type and id, send an empty fields[offer]= query parameter, it should have the effect of not showing any other field. This is not explicitly stated in the specification, but it’s implicitly implied by the fact that having one single fields[offer]=foo field would include type and id too.

Relationships are at the same level of attributes (both are fields) so fields[offer]=author won’t include the comments in relationships. This is explicitly covered by the specification.

On the other side, meta and links are not covered by any of this. On the other side, there is no requirement for links or meta to be always the same. My personal strategy is to include always links in single resources, never in included resources, and conditionally (depending on the selected fields) on listings. This is because links in my case are costly to calculate (they depend on permissions and system state, and might need additional queries to the DB).