JSONAPI: Update relationships including attributes

2.7k views Asked by At

I have a nested object in my SQLAlchemy table, produced with Marshmallow's nested schema feature. For example, an articles object GET response would include an author (a User type) object along with it.

I know that the JSONAPI spec already allows updating relationships. However, often I would like to update an article as well with its nested objects in one call (POST requests of articles that include a new author will automatically create the author). Is it possible to make a PATCH request that includes the resources of a relationship object that doesn't yet exist?

So instead of just this:

PATCH /articles/1 HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "type": "articles",
    "id": "1",
    "relationships": {
      "author": {
        "data": { "type": "people", "id": "1" }
      }
    }
  }
}

It'd be ideal to pass this in to create a new author if there didn't exist one (this is not my actual use case, but I have a similar real life need):

PATCH /articles/1 HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
{
  "data": {
    "type": "articles",
    "id": "1",
    "relationships": {
      "author": {
        "data": { "type": "people", "id": "1", "attributes": {"name": "new author":, "articles_written": 1} }
      }
    }
  }
}

Is this at all possible or have suggestions on what REST frameworks can support this, or would it be totally against the JSON API spec?

1

There are 1 answers

3
jelhan On

Updating multiple resources at once is not possible with JSON API spec v1.0. But there are several suggestions how to do that. Both official extensions which are not supported anymore aimed to support creating, updating or deleting multiple request at once. Also there is an open pull request which would introduce operations to upcoming JSON API spec v1.2.

For example a request updating two resources at once using suggested operations[1] would look like this:

PATCH /bulk HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json

{
  "operations": [{
    "op": "update",
    "ref": {
      "type": "articles",
      "id": "1"
    },
    "data": {
      "type": "articles",
      "id": "1",
      "attributes": {
        "title": "Updated title of articles 1"
      }
    }
  }, {
    "op": "update",
    "ref": {
      "type": "people",
      "id": "2"
    },
    "data": {
      "type": "people",
      "id": "2",
      "attributes": {
        "name": "updated name of author 2"
      }
    }
  }]
}

This would update title attribute of article with id 1 and name attribute of person resource with id 2 in a single transaction.

An request to update a to-one relationship and updating the related resource in a singular transaction would look like this:

PATCH /bulk HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json

{
  "operations": [{
    "op": "update",
    "ref": {
      "type": "articles",
      "id": "1",
      "relationship": "author"
    },
    "data": {
      "type": "people",
      "id": "2"
    }
  }, {
    "op": "update",
    "ref": {
      "type": "people",
      "id": "2"
    },
    "data": {
      "type": "people",
      "id": "2",
      "attributes": {
        "name": "updated name of author 2"
      }
    }
  }]
}

This is a request which creates a new person and associate it as author to an existing article with id 1:

PATCH /bulk HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json

{
  "operations": [{
    "op": "add",
    "ref": {
      "type": "people"
    },
    "data": {
      "type": "people",
      "lid": "a",
      "attributes": {
        "name": "name of new person"
      }
    }
  }, {
    "op": "update",
    "ref": {
      "type": "articles",
      "id": "1",
      "relationship": "author"
    },
    "data": {
      "type": "people",
      "lid": "a"
    }
  }]
}

Note that the order is important. Operations must be performed in order by server. So the resource has to be created before it could be associated to an existing one. To express the relationship we use a local id (lid).

Please note that operations should only be used if a request has to be performed transactionally. If each of the included operations could be executed atomically, singular requests should be used.

Accordingly to implementation list provided on jsonapi.org there are some libraries supporting Patch extension.

Update: Operations were not included in release candidate for JSON API v1.1. It's planned for v1.2 now. The author of the pull request, which is also one of the maintainers of the spec, said that "operations are now the highest priority". A release candidate for v1.2 including Operations may be shipped "RC within a few months of 1.1 final."

[1] Introducing operations to JSON API v1.2 is currently only suggested but not merged. There may be breaking changes. Read the related pull request before implementation.