目次
はじめに
今、モックAPIサーバーをオープンソースで公開しようとしてJSONをブラウザーやREST APIコでJSONデータを更新できるコードを作成中に複数レコード(mutiple recods)を登録・更新・削除はREST APIでどう実装したら良いのか?について調べてみ話です。通常はRESTful API設計で十分ですが、今回は主にデバッグをサポートするAPIを作る予定なので調査してみました。
各社のREST API
Saleforce
クラウド型のビジネスアプリケーションで有名なSaleforceのRST APIの例です。
urlパスにあるcompositeやtreeという単顔が入って複数の意味合いを持ってるようです。
既存リソースから独立して別途専用のAPIの入口を用意いるのがわかりますね。
Create Multiple Records
curl https://yourInstance.salesforce.com/services/data/v53.0/composite/tree/Account/ -H "Authorization: Bearer token -H "Content-Type: application/json" -d "@newrecords.json"
{ "records" :[{ "attributes" : {"type" : "Account", "referenceId" : "ref1"}, "name" : "SampleAccount1", "phone" : "1111111111", "website" : "www.salesforce.com", "numberOfEmployees" : "100", "industry" : "Banking" },{ "attributes" : {"type" : "Account", "referenceId" : "ref2"}, "name" : "SampleAccount2", "phone" : "2222222222", "website" : "www.salesforce2.com", "numberOfEmployees" : "250", "industry" : "Banking" },{ "attributes" : {"type" : "Account", "referenceId" : "ref3"}, "name" : "SampleAccount3", "phone" : "3333333333", "website" : "www.salesforce3.com", "numberOfEmployees" : "52000", "industry" : "Banking" },{ "attributes" : {"type" : "Account", "referenceId" : "ref4"}, "name" : "SampleAccount4", "phone" : "4444444444", "website" : "www.salesforce4.com", "numberOfEmployees" : "2500", "industry" : "Banking" }] }
Oracle
有償で堅牢性は高いデータベースです。
query stringでactionを与えるのが特徴ですね。
actionに渡すパラメータを変更することで拡張性がよさそうですが、その分、ドキュメントにわかりやすく書かないとハマりそうです。
Delete multiple profile list records in a single request
Sample request endpoint:
POST /rest/api/v1.3/lists/MyExampleProfileList/members?action=delete
Sample request body:
{ "queryAttribute" : "e", "ids" : ["recipient1@example.com", "recipient2@example.com"] }
HTTPS response status code: 200 OK
{ "recordData": {"fieldNames": ["EMAIL_ADDRESS_"], "records": [ ["recipient1@example.com "], ["DELETEFAILED: No Records found for EMAIL_ADDRESS_ = recipient2@example.com"] ], "mapTemplateName": null }, "mergeRule": {"textValue": null, "htmlValue": null, "optinValue": null, "insertOnNoMatch": false, "updateOnMatch": null, "matchColumnName1": null, "matchColumnName2": null, "matchOperator": null, "optoutValue": null, "rejectRecordIfChannelEmpty": null, "defaultPermissionStatus": null, "matchColumnName3": null }, "links": [ { "rel": "self", "href": "/rest/api/v1.3/lists/MyExampleProfileList/members?action=delete", "method": "POST" }, { "rel": "retrieveMultipleListRecipients", "href": "/rest/api/v1.3/lists/MyExampleProfileList/members?action=get", "method": "POST" }, { "rel": "mergeListRecipients", "href": "/rest/api/v1.3/lists/MyExampleProfileList/members", "method": "POST" } ] }
IBM
パソコン会社で有名です。IBM CloudでWatsonと翻訳APIなど遊んだ記憶があります(笑)
既存workordersというメソッドにbulkというAPIを追加したようです。
Saleforceと違って既存リソースから独立せず既存リソースにbulkというリソースを拡張したのが特徴です。
Bulk deleting work orders using REST APIs
https://www.ibm.com/docs/en/supply-chain-insight?topic=apis-bulk-deleting-work-orders
GETメソッド
https://api.ibm.com/scinsights/run/api/workorders
POSTメソッド
https://api.ibm.com/scinsights/run/api/workorders/bulk
body
{ "allOrNothing": false, "deletes": ["ID1", "ID2", "ID3"] }
世界で一番有名な検索エンジン会社です。プログラマにとっては神様の存在!!
GoogleはAPI設計ガイドがあってここでカスタムメソッドのガイドが載ってありました。
https://cloud.google.com/apis/design/custom_methods
下記のように既存リソースを拡張して:customVerbのようにすることです。
https://service.name/v1/some/resource/name:customVerb
このやり方はOracleのやり方と似てますね。
/ではなく:にした理由もちゃんと書いてありました。
カスタム動詞とリソース名を区切るために / ではなく : を使用する理由は、任意のパスに対応するためです。たとえば、ファイルの削除の取り消しは POST /files/a/long/file/name:undelete にマッピングされます。
https://cloud.google.com/apis/design/custom_methods
メソッド名 | カスタム動詞 | HTTP 動詞 | 注 |
Cancel | :cancel | POST | 未処理のオペレーション(operations.cancel など)をキャンセルします。 |
BatchGet | :batchGet | GET | 複数のリソースをバッチで取得します。(詳しくは、リストの説明をご覧ください)。 |
Move | :move | POST | folders.move など、リソースを別の親に移動します。 |
Search | :search | GET | services.search など、List セマンティクスに準拠していないデータを取得するための List の代替。 |
Undelete | :undelete | POST | 以前に削除したリソース(services.undelete など)を復元します。推奨保持期間は 30 日です。 |
さすがGoogle様リソース削除復元のAPIまで用意してくれてますね。
Microsoft
皆さんWindows使ってますよね?そのWindowsを開発した会社です。
REST APIではなくGraph APIだから番外編です。
複数リクエストをリクエストパラメータにまとめていっぺんに呼び出す方式のようです。
Graph APIはRESTful APIと概念が異なるのでこういった使い方もOKというわけです。
Combine multiple requests in one HTTP call using JSON batching
https://docs.microsoft.com/en-us/graph/json-batching
リクエスト
POST https://graph.microsoft.com/v1.0/$batch Accept: application/json Content-Type: application/json
{ "requests": [ { "id": "1", "method": "GET", "url": "/me/drive/root:/{file}:/content" }, { "id": "2", "method": "GET", "url": "/me/planner/tasks" }, { "id": "3", "method": "GET", "url": "/groups/{id}/events" }, { "id": "4", "url": "/me", "method": "PATCH", "body": { "city" : "Redmond" }, "headers": { "Content-Type": "application/json" } } ] }
レスポンス
200 OK Content-Type: application/json
{ "responses": [ { "id": "1", "status": 302, "headers": { "location": "https://b0mpua-by3301.files.1drv.com/y23vmagahszhxzlcvhasdhasghasodfi" } }, { "id": "3", "status": 401, "body": { "error": { "code": "Forbidden", "message": "..." } } }, { "id": "2", "status": 200, "body": { "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(microsoft.graph.plannerTask)", "value": [] } }, { "id": "4", "status": 204, "body": null } ] }
まとめ
REST APIでは複数レコードの扱いについてなにも決まっていないので会社ごとによってAPI定義やルールが異ります。個人的には方法が幾つありますが、大きく3つにまとめると
- 上記で紹介した定義やルールの中で好きなやり方でカスタムメソッドを実装する
- RESTfulではないのでドキュメントを充実に書くべき
- 特に理由がなければGoogle方式が一番良さそうです。
- 断念して単一リソースを複数呼び出す
- 実装が一番簡単
- クライアントでSleepをかますことでAPI負荷調整も可能
- 効率やパフォーマンスがよくない(クライアントがよしなに実装するしかない)
- REST APIを諦めてGraph APIに移行
- RESTful APIと概念が異なって学習コストが発生
- 要件によるが、Bulk InsertやBulk Deleteなどが必要であればGraph APIの方が個人的におすすめです。
検索キーワードはBatchやBulkなどよく出ます。