Search code examples
json-apicrnk

Sparse fields on complex JSON API attributes


According to #document-resource-object-attributes it is allowed to have 'complex' values for attributes, i.e. any valid JSON value.

With #fetching-sparse-fieldsets it is possible to select a subset of the content. However, all examples are matching the attribute name.

For example:

{
  "data": [
    {
      "type": "dogs",
      "id": "3f02e",
      "attributes": {
        "name": "doggy",
        "body": {
          "head": "small",
          "legs": [
            {
              "position": "front",
              "side": "right"
            },
            {
              "position": "front",
              "side": "left"
            }
          ],
          "fur": {
            "color": "brown"
          }
        }
      }
    }
  ]

In the result I am only interested in the name, body.head and body.fur.color.

What would be a correct way to solve this (preferably without requiring relations, since this data is valid)?


Solution

  • JSON:API's Sparse Fieldsets feature allows to request only specific fields of a resource:

    A client MAY request that an endpoint return only specific fields in the response on a per-type basis by including a fields[TYPE] parameter.

    https://jsonapi.org/format/#fetching-sparse-fieldsets

    A field is either an attribute or a relationship in JSON:API:

    A resource object’s attributes and its relationships are collectively called its “fields”.

    https://jsonapi.org/format/#document-resource-object-fields

    Sparse Fieldsets are not meant to have an impact on the value of an attribute or a relationship. If you have such a need you shouldn't model the data as a complex value but expose it as a separate resource.

    Please note that there is no need that your database schema and the exposed resources by your API are the same. Actually it often makes sense to not have a 1-to-1 relationship between database tables and resources in your JSON:API.

    Don't be afraid of having multiple resources. It's often much better for the long-term than having one resource with complex objects:

    • You can include the related resource (e.g. dog-bodies, dog-legs, dog-furs in your case) by default.
    • You can generate the IDs for that resources automatically based on the persisted ID of a parent resource.
    • You can have much stricter constraints and easier documentation for you API if having separate resources.
    • You can reduce the risk of collisions as you can support updating specific parts (e.g. color attribute of a dog-furs) rather than replacing the full body value of a dogs resource.

    The main drawback that I see currently with having multiple resources instead of one is the limitation that you can't create or update more than one resource in the same request with JSON:API v1.0. But it's very likely that the upcoming v1.1 won't have that limitation anymore. An official existing called Atomic Operations is proposed for that use case by a member of the core team working on the spec.