はじめに

今、モックAPIサーバーをオープンソースで公開しようとしてJSONをブラウザーやREST APIコでJSONデータを更新できるコードを作成中に複数レコード(mutiple recods)を登録・更新・削除はREST APIでどう実装したら良いのか?について調べてみ話です。通常はRESTful API設計で十分ですが、今回は主にデバッグをサポートするAPIを作る予定なので調査してみました。

各社のREST API

Saleforce

クラウド型のビジネスアプリケーションで有名なSaleforceのRST APIの例です。
urlパスにあるcompositetreeという単顔が入って複数の意味合いを持ってるようです。
既存リソースから独立して別途専用のAPIの入口を用意いるのがわかりますね。

https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_composite_sobject_tree_flat.htm

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

https://docs.oracle.com/en/cloud/saas/marketing/responsys-develop/API/REST/api-v1.3-lists-listName-members-post-actionDelete.htm

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

世界で一番有名な検索エンジン会社です。プログラマにとっては神様の存在!!
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つにまとめると

  1. 上記で紹介した定義やルールの中で好きなやり方でカスタムメソッドを実装する
    • RESTfulではないのでドキュメントを充実に書くべき
    • 特に理由がなければGoogle方式が一番良さそうです。
  2. 断念して単一リソースを複数呼び出す
    • 実装が一番簡単
    • クライアントでSleepをかますことでAPI負荷調整も可能
    • 効率やパフォーマンスがよくない(クライアントがよしなに実装するしかない)
  3. REST APIを諦めてGraph APIに移行
    1. RESTful APIと概念が異なって学習コストが発生
    2. 要件によるが、Bulk InsertやBulk Deleteなどが必要であればGraph APIの方が個人的におすすめです。

検索キーワードはBatchBulkなどよく出ます。