Salesforce Composite API を活用した高パフォーマンスな連携の実装

こんにちは、Salesforce 統合エンジニアです。私の日々の業務は、Salesforce と外部システム間のデータフローを設計し、シームレスで効率的な連携を構築することです。数多くの連携プロジェクトに携わる中で、パフォーマンスとトランザクション管理は常に重要な課題となります。本日は、これらの課題を解決するための強力なツール、Salesforce の Composite API (コンポジット API) について、私の経験を交えながら詳しく解説していきます。


背景と適用シナリオ

従来の REST API を使用した連携を考えてみましょう。例えば、「新規取引先を作成し、その直後にその取引先に関連する担当者を作成する」という一般的なビジネス要件があります。このプロセスを標準的な API コールで実装すると、以下のようになります。

1. API コール 1: 取引先 (Account) を作成するために Salesforce へリクエストを送信する。

2. API レスポンス 1: Salesforce が取引先を作成し、成功レスポンスと新しい取引先 ID を返す。

3. API コール 2: ステップ 2 で受け取った取引先 ID を使用して、担当者 (Contact) の作成リクエストを Salesforce へ送信する。

4. API レスポンス 2: Salesforce が担当者を作成し、成功レスポンスを返す。

このアプローチは機能しますが、クライアントとサーバー間で 2 回のネットワーク往復(ラウンドトリップ)が発生します。ネットワークの遅延は、全体の処理時間を大幅に増加させる可能性があります。また、もしステップ 3 の担当者作成が何らかの理由で失敗した場合、すでに作成されてしまった取引先をどう処理するか、というエラーハンドリングのロジックをクライアント側で実装する必要があり、複雑さが増します。

Composite API は、まさにこのようなシナリオのために設計されました。複数の API リクエストを 1 つの HTTP リクエストにまとめ、サーバー側で一連の操作として実行することができます。これにより、ネットワークのオーバーヘッドを劇的に削減し、さらに一連の操作を単一のトランザクションとして扱うことが可能になります。

原理の説明

Composite API は、一連の独立した REST API リクエストを単一の API コールにまとめるための REST リソースです。リクエストの本体 (body) に、実行したいサブリクエスト (subrequest) のコレクションを JSON 形式で含めます。

Composite API のリクエストは、以下の主要な要素で構成されます。

リクエストの構造

リクエストの本体は、主に 2 つのキーを持つ JSON オブジェクトです。

1. allOrNone: ブール値 (true または false) を指定します。このプロパティが `true` に設定されている場合、すべてのサブリクエストは単一のトランザクションとして扱われます。つまり、いずれか 1 つのサブリクエストでも失敗した場合、それまでに成功したすべてのサブリクエストの操作がロールバック(取り消し)されます。データの整合性を保つ上で非常に重要な機能です。`false` (デフォルト値) の場合は、各サブリクエストは独立して実行され、一部が失敗しても他の成功した操作はコミットされます。

2. compositeRequest: 実行したいサブリクエストの配列です。各サブリクエストは、以下の属性を持つ JSON オブジェクトです。

  • method: HTTP メソッド (`GET`, `POST`, `PATCH`, `DELETE` など)。
  • url: サブリクエストの REST API の URL (例: `/services/data/v59.0/sobjects/Account`)。
  • referenceId: このサブリクエストを後続のサブリクエストから参照するためのユニークな ID です。これにより、サブリクエスト間に依存関係を構築できます。
  • body: `POST` や `PATCH` リクエストで送信するデータ本体 (JSON 形式)。

サブリクエスト間の依存関係

Composite API の最も強力な機能の一つが、`referenceId` (参照 ID) を使ったサブリクエスト間の依存関係の構築です。例えば、最初のサブリクエストで新しい取引先を作成し、その `referenceId` を `"newAccount"` とします。後続のサブリクエストでは、`"@{newAccount.id}"` という構文を使用することで、最初のサブリクエストで作成された取引先の ID を動的に取得し、利用することができます。これにより、「取引先を作成し、その ID を使って担当者を作成する」という一連の処理を、1 回の API コールで完結させることが可能になります。

サンプルコード

ここでは、Salesforce 公式ドキュメントに記載されている例を基に、Composite API の具体的な使用方法を見ていきましょう。この例では、以下の 3 つの操作を 1 つのトランザクションとして実行します。

  1. 新しい取引先 (Account) を作成する。
  2. 作成した取引先に関連する新しい担当者 (Contact) を作成する。
  3. 最初に作成した取引先の `Description` 項目を更新する。

リクエストの例

以下は、Salesforce へ送信する Composite API リクエストの JSON 本体です。

{
    "allOrNone" : true,
    "compositeRequest" : [{
        "method" : "POST",
        "url" : "/services/data/v59.0/sobjects/Account",
        "referenceId" : "refAccount",
        "body" : { "Name" : "Salesforce" }
    },{
        "method" : "POST",
        "url" : "/services/data/v59.0/sobjects/Contact",
        "referenceId" : "refContact",
        "body" : {
            "LastName" : "Benioff",
            "AccountId" : "@{refAccount.id}"
        }
    },{
        "method": "PATCH",
        "url" : "/services/data/v59.0/sobjects/Account/@{refAccount.id}",
        "body": {
            "Description" : "Salesforce is a cloud-based software company."
        }
    }]
}

コードの解説

  • allOrNone: true
    このリクエスト全体が 1 つのトランザクションとして扱われます。3 つのサブリクエストのうち 1 つでも失敗すれば、すべての操作がロールバックされます。
  • 1 番目のサブリクエスト (referenceId: "refAccount")
    `method` が `POST`、`url` が `/services/data/v59.0/sobjects/Account` となっているため、新しい取引先を作成するリクエストです。`body` で `Name` を "Salesforce" と指定しています。この操作に `refAccount` という参照 ID を割り当てています。
  • 2 番目のサブリクエスト (referenceId: "refContact")
    新しい担当者を作成するリクエストです。ここが重要なポイントですが、`body` 内の `AccountId` 項目に `"@{refAccount.id}"` という値が指定されています。これは、「`refAccount` という `referenceId` を持つサブリクエストの結果(この場合は新しく作成された取引先)の `id` プロパティを参照する」という意味です。これにより、ネットワークを介して ID を受け取ることなく、サーバー内部で直接関連付けが行われます。
  • 3 番目のサリブクエスト
    `method` が `PATCH` となっており、既存のレコードを更新するリクエストです。`url` にも `"@{refAccount.id}"` が使われており、1 番目のサブリクエストで作成した取引先を特定しています。そして `body` でその取引先の `Description` 項目を更新しています。

レスポンスの例

上記のリクエストが成功した場合、Salesforce から以下のようなレスポンスが返されます。

{
    "compositeResponse" : [{
        "body" : {
            "id" : "001xx000003DHPFPA4",
            "success" : true,
            "errors" : [ ]
        },
        "httpHeaders" : {
            "Location" : "/services/data/v59.0/sobjects/Account/001xx000003DHPFPA4"
        },
        "httpStatusCode" : 201,
        "referenceId" : "refAccount"
    },{
        "body" : {
            "id" : "003xx000002JDEhAAO",
            "success" : true,
            "errors" : [ ]
        },
        "httpHeaders" : {
            "Location" : "/services/data/v59.0/sobjects/Contact/003xx000002JDEhAAO"
        },
        "httpStatusCode" : 201,
        "referenceId" : "refContact"
    },{
        "body" : null,
        "httpHeaders" : { },
        "httpStatusCode" : 204,
        "referenceId" : null
    }]
}

レスポンスの解説

レスポンスは `compositeResponse` というキーを持つ配列で、各要素がリクエストの各サブリクエストに対応しています。各要素には `httpStatusCode` (HTTP ステータスコード)、`body` (レスポンス本体)、`referenceId` が含まれており、どのサブリクエストの結果なのかを特定できます。このレスポンスから、新しく作成された取引先と担当者の ID を取得することができます。


注意事項

Composite API は非常に強力ですが、利用する際にはいくつかの注意点があります。

権限とアクセス制御

Composite API を実行するユーザーは、リクエストに含まれるすべてのサブリクエストで操作されるオブジェクトや項目に対する適切な権限 (CRUD/FLS) を持っている必要があります。いずれか一つでも権限が不足している場合、そのサブリクエストは失敗し、`allOrNone` が `true` であればトランザクション全体がロールバックされます。

API 制限とガバナ制限

  • API コール数: Composite API のリクエストは、全体で 1 回の API コールとしてカウントされます。これは API コール数を節約する上で大きな利点です。
  • サブリクエスト数: 1 回の Composite API リクエストに含めることができるサブリクエストの数は 25 個までです。
  • ガバナ制限: 1 回の API コールとしてカウントされますが、内部的には Salesforce のガバナ制限 (例: 1 トランザクションあたりの DML ステートメント数など) が適用されます。多数のサブリクエストを実行する場合は、これらの内部的な制限に抵触しないか注意が必要です。

エラー処理

エラー処理は統合の品質を左右する重要な要素です。`allOrNone` の設定によって、エラー発生時の挙動が大きく異なります。

  • `allOrNone` が `true` の場合:
    いずれかのサブリクエストが失敗すると、トランザクション全体がロールバックされます。レスポンスでは、失敗したサブリクエストの `httpStatusCode` が 4xx または 5xx となり、`body` にエラーメッセージが含まれます。他のサブリクエストはロールバックされた旨が示されます。クライアント側では、失敗したリクエストを特定し、リトライやユーザーへの通知といった処理を実装する必要があります。
  • `allOrNone` が `false` の場合:
    各サブリクエストが独立して実行されます。クライアントは、レスポンス配列の各要素を個別にチェックし、どのリクエストが成功し、どれが失敗したかを判断する必要があります。データの部分的なコミットを許容するシナリオで有効ですが、クライアント側のエラーハンドリングとデータ整合性の管理はより複雑になります。

まとめとベストプラクティス

Composite API は、Salesforce 統合におけるパフォーマンスと信頼性を向上させるための不可欠なツールです。複数の操作を単一のネットワークリクエストにまとめることでレイテンシを削減し、トランザクション機能によってデータの整合性を保証します。

統合エンジニアとして、私は以下のベストプラクティスを推奨します。

  1. 論理的な単位でまとめる: 互いに関連し、ビジネスプロセスとして 1 つの単位と見なせる操作を Composite API にまとめましょう。無関係な操作を無理に詰め込むのは避けるべきです。
  2. トランザクション整合性を優先する: データの整合性が重要な場合は、必ず `allOrNone` を `true` に設定してください。これが最も一般的で安全な使い方です。
  3. 大量データには Bulk API を検討する: Composite API は、数十件程度のレコードを対象としたトランザクション処理に最適です。数百、数千件以上の大量データを一括で処理する場合は、Bulk API の方が適しています。
  4. 堅牢なエラーハンドリングを実装する: レスポンスを正しく解析し、サブリクエストごとの成功・失敗を適切に処理するロジックをクライアント側に必ず実装してください。
  5. `referenceId` を活用する: 依存関係のある一連の操作には、積極的に `referenceId` を利用し、API の往復をなくしましょう。これにより、コードがシンプルになり、パフォーマンスが向上します。

Composite API を適切に理解し活用することで、より高速で、信頼性が高く、保守しやすい Salesforce 連携ソリューションを構築することができます。

コメント