Atomic Operations
This extension provides a means to perform multiple “operations” in a linear and atomic manner. Operations are a serialized form of the mutations allowed in the base JSON:API specification.
Clients can send an array of operations in a single request. This extension guarantees that those operations will be processed in order and will either completely succeed or fail together.
URI
This extension has the URI https://jsonapi.org/ext/atomic
.
Namespace
This extension uses the namespace atomic
.
Note: JSON:API extensions can only introduce new document members using a reserved namespace as a prefix.
Document Structure
A document that supports this extension MAY include any of the top-level
members allowed by the base specification with the exception of data
and
included
, which MUST NOT be included.
In addition, such a document MAY include either of the following members, but not both:
-
atomic:operations
- an array of one or more operation objects. -
atomic:results
- an array of one or more result objects.
If either atomic:operations
or atomic:results
is present, the errors
member MUST NOT be included in the same document.
Operation Objects
An operation object MUST contain the following member:
-
op
: an operation code, expressed as a string, that indicates the type of operation to perform. The value MUST be one of the following:"add"
: creates a new resource or relationship"update"
: updates a resource or relationship"remove"
: removes a resource or relationship
An operation object MAY contain either of the following members, but not both, to specify the target of the operation:
-
ref
: an object that MUST contain one of the following combinations of members:type
andid
: to target an individual resource.type
andlid
: to target an individual resource that has been assigned a local identity (lid
) in a prior operation object.type
,id
, andrelationship
: to target the relationship of an individual resource.type
,lid
, andrelationship
: to target the relationship of an individual resource that has been assigned a local identity (lid
) in a prior operation object.
-
href
: a string that contains a URI-reference [RFC3986 Section 4.1] that identifies the target of the operation.
Some specific types of operations require inclusion of ref
or href
, as
described below.
An operation object MAY also contain any of the following members:
-
data
: the operation’s “primary data”. -
meta
: a meta object that contains non-standard meta-information about the operation.
Different members are required for processing different types of operations, as described below.
Result Objects
An operation result object MAY contain any of the following members:
-
data
: the “primary data” resulting from the operation. -
meta
: a meta object that contains non-standard meta-information about the result.
An empty result object ({}
) is acceptable for operations that are not required
to return data
.
Processing
All HTTP requests sent with this extension MUST be issued with POST
.
A server MUST perform operations in the order they appear in the
atomic:operations
array.
A server MUST perform all operations atomically, so that a failure to perform any operation MUST invalidate any effects of preceding operations.
A server MUST respond to a successful operations request with 200 OK
if a
response document is returned. An array of result objects
MUST be returned in a top-level atomic:results
member. The results array
MUST be the same length as the requested operations array and each result
MUST correspond positionally to its associated operation.
A server MAY respond to successful requests that include operations with
204 No Content
and no response document if no operations are required to
return data
.
Processing Errors
If any operation in a request fails, the server MUST respond as described
in the base specification. An array of one or
more error objects SHOULD be returned, each with
a source
member that contains a pointer
to the source of the problem in the
request document.
If a requested operation is malformed or incomplete, then a server MUST
respond with a 400 Bad Request
and SHOULD include a document with a
top-level errors
member that contains an error object. The error object
SHOULD include a source
member with a pointer
to the invalid operation.
If an operation is properly formed, but cannot be processed by a server, the
server MUST respond with a 400 Bad Request
or a more appropriate error
response (e.g. 409 Conflict
or 422 Unprocessable Entity
) and SHOULD
include a document with a top-level errors
member that contains one or more
error objects that provide further details.
Processing Specific Operations
The following sections describe how to process specific operations.
Creating Resources
An operation that creates a resource MAY target a resource collection
through the operation’s href
member.
The operation MUST include an op
code of "add"
as well as a resource
object as data
. The resource object MUST contain at least a type
member.
For example:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:operations": [{
"op": "add",
"href": "/blogPosts",
"data": {
"type": "articles",
"attributes": {
"title": "JSON API paints my bikeshed!"
}
}
}]
}
Note that href
is used in this example to target a resource collection,
blogPosts
in this case, that is distinct from the type
of the resource.
The usage of href
is entirely optional for this extension, but could be
required by an individual implementation.
Responses
If the server is able to create a resource with a client-generated ID and its
representation would be identical to the resource in the operation, then the
server MAY return a result with no data
member or it MAY return a
result that contains the created resource as data
.
In all other cases in which the server is able to successfully create the
resource, the server MUST return a result that contains the created resource
as data
.
For example:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:results": [{
"data": {
"links": {
"self": "http://example.com/blogPosts/13"
},
"type": "articles",
"id": "13",
"attributes": {
"title": "JSON API paints my bikeshed!"
}
}
}]
}
If the server is unable to create the resource, an appropriate error response
MUST be returned and a response document SHOULD be returned that
contains a top-level errors
as described above.
Updating Resources
An operation that updates a resource MAY target that resource through the
operation’s ref
or href
members, but not both.
The operation MUST include an op
code of "update"
.
For example:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:operations": [{
"op": "update",
"data": {
"type": "articles",
"id": "13",
"attributes": {
"title": "To TDD or Not"
}
}
}]
}
Responses
If a server accepts an update but also changes the resource in ways other than
those specified by the request (for example, updating the updatedAt
attribute
or a computed sha
), then the server MUST return a result that includes a
representation of the updated resource as data
.
If a server accepts an update and doesn’t update any fields besides those
provided, the server MUST return a result that includes either no data
or
a representation of the resource as data
or, if all results are empty, the
server MAY respond with 204 No Content
and no document.
If a server is unable to update the resource, an appropriate error response
MUST be returned and a response document SHOULD be returned that
contains a top-level errors
as described above.
Deleting Resources
An operation that deletes a resource MUST target that resource through the
operation’s ref
or href
members, but not both.
The operation MUST include an op
code of "remove"
.
For example:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:operations": [{
"op": "remove",
"ref": {
"type": "articles",
"id": "13"
}
}]
}
Responses
If a server is able to delete the resource, the server MUST return a result
with no data
or, if all results are empty, the server MAY respond with
204 No Content
and no document.
If a server is unable to delete the resource, an appropriate error response
MUST be returned and a response document SHOULD be returned that
contains a top-level errors
as described above.
Updating To-One Relationships
An operation that updates a resource’s to-one relationship MUST target that
relationship through the operation’s ref
or href
members, but not both.
The operation MUST include an op
code of "update"
.
For example, the following request assigns a to-one relationship:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:operations": [{
"op": "update",
"ref": {
"type": "articles",
"id": "13",
"relationship": "author"
},
"data": {
"type": "people",
"id": "9"
}
}]
}
And the following request clears a to-one relationship:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:operations": [{
"op": "update",
"ref": {
"type": "articles",
"id": "13",
"relationship": "author"
},
"data": null
}]
}
Responses
If a server is able to update the relationship, the server MUST return a
result with no data
or, if all results are empty, the server MAY respond
with 204 No Content
and no document.
If a server is unable to update the relationship, an appropriate error response
MUST be returned and a response document SHOULD be returned that
contains a top-level errors
as described above.
Updating To-Many Relationships
An operation that updates a resource’s to-many relationship MUST target that
relationship through the operation’s ref
or href
members, but not both.
To add members to a to-many relationship, the operation MUST include an
op
code of "add"
. For example:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:operations": [{
"op": "add",
"ref": {
"type": "articles",
"id": "1",
"relationship": "comments"
},
"data": [
{ "type": "comments", "id": "123" }
]
}]
}
To replace all the members of a to-many relationship, the operation MUST
include an op
code of "update"
. For example:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:operations": [{
"op": "update",
"ref": {
"type": "articles",
"id": "1",
"relationship": "tags"
},
"data": [
{ "type": "tags", "id": "2" },
{ "type": "tags", "id": "3" }
]
}]
}
To remove members from a to-many relationship, the operation MUST include an
op
code of "remove"
. For example:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:operations": [{
"op": "remove",
"ref": {
"type": "articles",
"id": "1",
"relationship": "comments"
},
"data": [
{ "type": "comments", "id": "12" },
{ "type": "comments", "id": "13" }
]
}]
}
Responses
If a server is able to update the relationship, the server MUST return a
result with no data
or, if all results are empty, the server MAY respond
with 204 No Content
and no document.
If a server is unable to update the relationship, an appropriate error response
MUST be returned and a response document SHOULD be returned that
contains a top-level errors
as described above.
Processing Multiple Operations
The above examples all perform a single operation that aligns with an equivalent singular request from the base spec. Yet, the primary value of this extension is that unlocks the ability to perform more than one action linearly and atomically.
The following example adds two resources and creates a relationship between them in a single request:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:operations": [{
"op": "add",
"data": {
"type": "authors",
"id": "acb2ebd6-ed30-4877-80ce-52a14d77d470",
"attributes": {
"name": "dgeb"
}
}
}, {
"op": "add",
"data": {
"type": "articles",
"id": "bb3ad581-806f-4237-b748-f2ea0261845c",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"relationships": {
"author": {
"data": {
"type": "authors",
"id": "acb2ebd6-ed30-4877-80ce-52a14d77d470"
}
}
}
}
}]
}
A server might respond to this request as follows:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:results": [{
"data": {
"links": {
"self": "http://example.com/authors/acb2ebd6-ed30-4877-80ce-52a14d77d470"
},
"type": "authors",
"id": "acb2ebd6-ed30-4877-80ce-52a14d77d470",
"attributes": {
"name": "dgeb"
}
}
}, {
"data": {
"links": {
"self": "http://example.com/articles/bb3ad581-806f-4237-b748-f2ea0261845c"
},
"type": "articles",
"id": "bb3ad581-806f-4237-b748-f2ea0261845c",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/bb3ad581-806f-4237-b748-f2ea0261845c/relationships/author",
"related": "http://example.com/articles/bb3ad581-806f-4237-b748-f2ea0261845c/author"
}
}
}
}
}]
}
Note that this operations request could also have been structured to add the author, then add the article, then add the relationship between them.
Also note that local identities, i.e.
lid
members, are particularly useful for requests that involve multiple operations. Local identities can be used to associate resources that have not yet been assigned anid
.