アトミック操作
この拡張機能は、複数の「操作」を線形かつアトミックに実行する手段を提供します。操作とは、基本JSON:API仕様で許可されている変更のシリアル化された形式です。
クライアントは、単一の要求で操作の配列を送信できます。この拡張機能は、これらの操作が順番に処理され、完全に成功するか、一緒に失敗するかのいずれかであることを保証します。
URI
この拡張機能のURIはhttps://jsonapi.dokyumento.jp/ext/atomic
です。
名前空間
この拡張機能は、atomic
名前空間を使用します。
注:JSON:API拡張機能は、予約された名前空間をプレフィックスとして使用して、新しいドキュメントメンバーを導入することのみができます。
ドキュメント構造
この拡張機能をサポートするドキュメントには、data
とincluded
を除く、基本仕様で許可されているトップレベルメンバーのいずれかを含めることができます(data
とincluded
は含めることはできません)。
さらに、そのようなドキュメントには、次のメンバーのいずれかを含めることができますが、両方を含めることはできません。
atomic:operations
またはatomic:results
のいずれかが存在する場合、errors
メンバーを同じドキュメントに含めることはできません。
操作オブジェクト
操作オブジェクトには、次のメンバーを含める必要があります。
-
op
:実行する操作の種類を示す、文字列で表される操作コード。値は次のいずれかでなければなりません。"add"
:新しいリソースまたはリレーションシップを作成します。"update"
:リソースまたはリレーションシップを更新します。"remove"
:リソースまたはリレーションシップを削除します。
操作オブジェクトには、操作のターゲットを指定するために、次のメンバーのいずれかを含めることができますが、両方を含めることはできません。
-
ref
:次のメンバーの組み合わせのいずれかを含まなければならないオブジェクト。type
とid
:個々のリソースをターゲットにします。type
とlid
:前の操作オブジェクトでローカルID(lid
)が割り当てられた個々のリソースをターゲットにします。type
、id
、およびrelationship
:個々のリソースのリレーションシップをターゲットにします。type
、lid
、およびrelationship
:前の操作オブジェクトでローカルID(lid
)が割り当てられた個々のリソースのリレーションシップをターゲットにします。
-
href
:操作のターゲットを識別するURI参照[RFC3986 セクション 4.1]を含む文字列。
特定の種類の操作では、以下のようにref
またはhref
を含める必要があります。
操作オブジェクトには、次のメンバーも入れることができます。
-
data
:操作の「主要なデータ」。 -
meta
:操作に関する非標準のメタ情報を含むメタオブジェクト。
以下のように、異なる種類の操作を処理するには、異なるメンバーが必要です。
結果オブジェクト
操作の結果オブジェクトには、次のメンバーのいずれかを含めることができます。
-
data
:操作の結果として得られる「主要なデータ」。 -
meta
:結果に関する非標準のメタ情報を含むメタオブジェクト。
data
を返す必要がない操作の場合、空の結果オブジェクト({}
)が許容されます。
処理
この拡張機能で送信されるすべてのHTTPリクエストは、POST
で発行する必要があります。
サーバーは、atomic:operations
配列に表示されている順序で操作を実行する必要があります。
サーバーは、すべての操作をアトミックに実行する必要があります。そのため、いずれかの操作の実行に失敗すると、先行する操作の影響が無効になります。
サーバーは、レスポンスドキュメントが返される場合、成功した操作リクエストに200 OK
で応答する必要があります。結果オブジェクトの配列は、トップレベルのatomic:results
メンバーで返す必要があります。結果配列の長さは要求された操作配列と同じでなければならず、各結果は、関連付けられた操作と位置的に対応していなければなりません。
操作にdata
を返す必要がない場合、サーバーは、data
を含む操作を含む成功したリクエストに204 No Content
とレスポンスドキュメントなしで応答することができます。
処理エラー
要求内のいずれかの操作が失敗した場合、サーバーは基本仕様に記載されているとおりにレスポンスする必要があります。1つ以上のエラーオブジェクトの配列を返す必要があります。各エラーオブジェクトには、要求ドキュメント内の問題のソースへのpointer
を含むsource
メンバーが含まれている必要があります。
要求された操作が不正な形式であるか、不完全である場合、サーバーは400 Bad Request
で応答し、トップレベルのerrors
メンバーを含むドキュメントを含める必要があります。そのerrors
メンバーにはエラーオブジェクトが含まれています。エラーオブジェクトには、無効な操作へのpointer
を含むsource
メンバーを含める必要があります。
操作が適切な形式であるにもかかわらず、サーバーで処理できない場合、サーバーは400 Bad Request
またはより適切なエラー応答(例:409 Conflict
または422 Unprocessable Entity
)で応答し、トップレベルのerrors
メンバーを含むドキュメントを含める必要があります。そのerrors
メンバーには、詳細を提供する1つ以上のエラーオブジェクトが含まれています。
特定の操作の処理
次のセクションでは、特定の操作の処理方法について説明します。
リソースの作成
リソースを作成する操作は、操作のhref
メンバーを介してリソースコレクションをターゲットにすることができます。
操作には、"add"
のop
コードと、data
としてのリソースオブジェクトを含める必要があります。リソースオブジェクトには、少なくともtype
メンバーを含める必要があります。
例:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
{
"atomic:operations": [{
"op": "add",
"href": "/blogPosts",
"data": {
"type": "articles",
"attributes": {
"title": "JSON API paints my bikeshed!"
}
}
}]
}
この例では、href
を使用して、リソースのtype
とは異なるリソースコレクション(この場合はblogPosts
)をターゲットにしていることに注意してください。href
の使用はこの拡張機能では完全にオプションですが、個々の実装では必須になる可能性があります。
応答
サーバーがクライアント生成のIDを使用してリソースを作成でき、その表現が操作内のリソースと同一である場合、サーバーはdata
メンバーを含まない結果を返すことができます。または、作成されたリソースをdata
として含む結果を返すことができます。
サーバーがリソースを正常に作成できるその他すべてのケースでは、サーバーは作成されたリソースをdata
として含む結果を返す必要があります。
例:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
{
"atomic:results": [{
"data": {
"links": {
"self": "http://example.com/blogPosts/13"
},
"type": "articles",
"id": "13",
"attributes": {
"title": "JSON API paints my bikeshed!"
}
}
}]
}
サーバーがリソースを作成できない場合、適切なエラー応答を返す必要があり、上記のようにトップレベルのerrors
を含む応答ドキュメントを返す必要があります。
リソースの更新
リソースを更新する操作は、操作のref
メンバーまたはhref
メンバーを介してそのリソースをターゲットにすることができますが、両方を使用することはできません。
操作には、"update"
のop
コードを含める必要があります。
例:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
{
"atomic:operations": [{
"op": "update",
"data": {
"type": "articles",
"id": "13",
"attributes": {
"title": "To TDD or Not"
}
}
}]
}
応答
サーバーが更新を受け入れるが、要求で指定されたもの以外の方法でリソースを変更する場合(たとえば、updatedAt
属性または計算されたsha
を更新する場合)、サーバーは更新されたリソースの表現をdata
として含む結果を返す必要があります。
サーバーが更新を受け入れ、提供されたもの以外のフィールドを更新しない場合、サーバーはdata
を含まない結果、またはリソースの表現をdata
として含む結果を返す必要があります。すべての結果が空の場合、サーバーは204 No Content
とドキュメントなしで応答することができます。
サーバーがリソースを更新できない場合、適切なエラー応答を返す必要があり、上記のようにトップレベルのerrors
を含む応答ドキュメントを返す必要があります。
リソースの削除
リソースを削除する操作は、操作のref
メンバーまたはhref
メンバーを介してそのリソースをターゲットにする必要がありますが、両方を使用することはできません。
操作には、"remove"
のop
コードを含める必要があります。
例:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
{
"atomic:operations": [{
"op": "remove",
"ref": {
"type": "articles",
"id": "13"
}
}]
}
応答
サーバーがリソースを削除できる場合、サーバーはdata
を含まない結果を返す必要があります。すべての結果が空の場合、サーバーは204 No Content
とドキュメントなしで応答することができます。
サーバーがリソースを削除できない場合、適切なエラー応答を返す必要があり、上記のようにトップレベルのerrors
を含む応答ドキュメントを返す必要があります。
1対1リレーションシップの更新
リソースの1対1リレーションシップを更新する操作は、操作のref
メンバーまたはhref
メンバーを介してそのリレーションシップをターゲットにする必要がありますが、両方を使用することはできません。
操作には、"update"
のop
コードを含める必要があります。
たとえば、次のリクエストは1対1のリレーションシップを割り当てます。
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
{
"atomic:operations": [{
"op": "update",
"ref": {
"type": "articles",
"id": "13",
"relationship": "author"
},
"data": {
"type": "people",
"id": "9"
}
}]
}
そして、次のリクエストは1対1のリレーションシップをクリアします。
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
{
"atomic:operations": [{
"op": "update",
"ref": {
"type": "articles",
"id": "13",
"relationship": "author"
},
"data": null
}]
}
応答
サーバーがリレーションシップの更新に成功した場合、サーバーはdata
を含まない結果を**必ず**返却する必要があります。すべての結果が空の場合、サーバーは204 No Content
とドキュメントなしで応答しても**構いません**。
サーバーがリレーションシップの更新に失敗した場合、適切なエラーレスポンスを**必ず**返却する必要があり、トップレベルのerrors
を含むレスポンスドキュメントを**返すことが推奨**されます(上記の説明を参照)。
多対多リレーションシップの更新
リソースの多対多リレーションシップを更新する操作は、操作のref
メンバまたはhref
メンバを介してそのリレーションシップをターゲットにする**必要**がありますが、両方同時に指定することはできません。
多対多リレーションシップにメンバを追加するには、操作にop
コードとして"add"
を含める**必要**があります。例:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
{
"atomic:operations": [{
"op": "add",
"ref": {
"type": "articles",
"id": "1",
"relationship": "comments"
},
"data": [
{ "type": "comments", "id": "123" }
]
}]
}
多対多リレーションシップのすべてのメンバを置き換えるには、操作にop
コードとして"update"
を含める**必要**があります。例:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
{
"atomic:operations": [{
"op": "update",
"ref": {
"type": "articles",
"id": "1",
"relationship": "tags"
},
"data": [
{ "type": "tags", "id": "2" },
{ "type": "tags", "id": "3" }
]
}]
}
多対多リレーションシップからメンバを削除するには、操作にop
コードとして"remove"
を含める**必要**があります。例:
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
{
"atomic:operations": [{
"op": "remove",
"ref": {
"type": "articles",
"id": "1",
"relationship": "comments"
},
"data": [
{ "type": "comments", "id": "12" },
{ "type": "comments", "id": "13" }
]
}]
}
応答
サーバーがリレーションシップの更新に成功した場合、サーバーはdata
を含まない結果を**必ず**返却する必要があります。すべての結果が空の場合、サーバーは204 No Content
とドキュメントなしで応答しても**構いません**。
サーバーがリレーションシップの更新に失敗した場合、適切なエラーレスポンスを**必ず**返却する必要があり、トップレベルのerrors
を含むレスポンスドキュメントを**返すことが推奨**されます(上記の説明を参照)。
複数操作の処理
上記の例はすべて、基本仕様からの同等の単一リクエストと一致する単一操作を実行します。しかし、この拡張の主な価値は、複数の操作を線形かつアトミックに実行できるようになることです。
次の例では、2つのリソースを追加し、それらの間にリレーションシップを単一のリクエストで作成します。
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/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"
}
}
}
}
}]
}
サーバーはこのリクエストに対して次のように応答する場合があります。
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/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"
}
}
}
}
}]
}
この操作リクエストは、著者を追加してから記事を追加し、次にそれらの間のリレーションシップを追加するように構成することもできたことに注意してください。
また、ローカルID(つまり、
lid
メンバ)は、複数の操作を含むリクエストに特に役立ちます。ローカルIDは、まだid
が割り当てられていないリソースを関連付けるために使用できます。