背景と適用シナリオ
Salesforce 開発者として、私たちは日常的に外部システムとの連携やデータ作成の効率化という課題に直面します。特に、Eコマースプラットフォームや基幹システム(ERP)から注文情報を取り込む際、標準的な REST API を使用すると、複数のオブジェクトにまたがるレコード作成は煩雑になりがちです。
典型的な注文処理では、「注文 (Order)」オブジェクトのレコードを1つ作成し、その注文に紐づく複数の「注文商品 (OrderItem)」レコードを作成する必要があります。従来の API コールでは、以下のようなステップを踏むことになります。
- API コール 1: 注文 (Order) レコードを作成する。
- レスポンスから、作成された注文の ID を取得する。
- API コール 2: 取得した注文 ID を使用して、1つ目の注文商品 (OrderItem) を作成する。
- API コール 3: 同じ注文 ID を使用して、2つ目の注文商品 (OrderItem) を作成する。
- (注文商品が N 個ある場合、この処理を N 回繰り返す)
このアプローチにはいくつかの欠点があります。まず、ネットワークの往復(ラウンドトリップ)が複数回発生するため、処理全体のレイテンシーが増加します。次に、Salesforce の API コール数制限をより多く消費してしまいます。さらに、一連の処理の途中でエラーが発生した場合、データの整合性を保つためのロールバック処理をクライアント側で実装する必要があり、複雑さが増します。
このような課題を解決するために Salesforce が提供しているのが、Composite REST API (複合 REST API) です。この API を利用することで、複数の独立した API リクエストを単一の HTTP リクエストにバンドルし、一度のコールで実行できます。これにより、注文レコードとその関連するすべての注文商品レコードをアトミックなトランザクションとして作成することが可能になり、パフォーマンス、効率、データ整合性を大幅に向上させることができます。
本記事では、Salesforce 開発者の視点から、Composite REST API を用いて注文と注文商品を効率的かつ同時に作成する方法について、その原理から具体的な実装例、注意点までを詳しく解説します。
原理説明
Composite REST API の中核的なコンセプトは、「サブリクエスト (subrequest)」の集合を単一の JSON ペイロードとして送信することにあります。Salesforce はこのペイロードを受け取ると、内部で各サブリクエストを順番に処理し、すべての結果をまとめて単一のレスポンスとして返します。
この仕組みを注文作成のシナリオで活用する鍵となるのが、referenceId と呼ばれるプロパティです。これは、一つのサブリクエストに一意の識別子を割り当てるためのものです。そして、後続のサブリクエスト内で、先行するサブリクエストの結果(例えば、新しく作成されたレコードの ID)を動的に参照することができます。
具体的な流れは以下の通りです。
1. リクエストボディの構築
リクエストボディのルートには、compositeRequest
というキーを持つ配列を定義します。この配列の各要素が、個別のサブリクエストを表す JSON オブジェクトになります。
2. 注文 (Order) 作成サブリクエスト
最初のサブリクエストとして、注文レコードを作成するリクエストを定義します。ここでのポイントは referenceId
を設定することです。例えば、"referenceId": "newOrder"
のように指定します。これにより、このリクエストは「newOrder」という名前で後から参照できるようになります。
3. 注文商品 (OrderItem) 作成サブリクエスト
次に、注文商品を作成するためのサブリクエストを定義します。注文商品は特定の注文に紐づく必要があるため、OrderId
項目に値を設定しなければなりません。ここで referenceId
が活躍します。OrderId
の値として、"@{newOrder.id}"
という特殊な構文を使用します。これは、「referenceId
が 'newOrder' のサブリクエストの結果として得られる id
項目を参照する」という意味になります。これにより、まだ存在しないレコードの ID を動的に指定し、リレーションを構築できます。
4. トランザクション制御
Composite API には allOrNone
という非常に重要なプロパティがあります。リクエストボディのルートでこのブール値を true
に設定すると、すべてのサブリクエストが単一のトランザクションとして扱われます。つまり、25個のサブリクエストのうち1つでも失敗した場合、それまでに行われたすべてのデータベース操作が自動的にロールバックされます。これにより、データの整合性が保証され、中途半端な状態(注文ヘッダーだけ作成されて明細がない、など)に陥ることを防げます。
この referenceId
と allOrNone
の組み合わせにより、開発者はクライアント側で複雑な状態管理やエラー処理ロジックを実装することなく、関連するレコード群を安全かつ効率的に作成することができるのです。
示例コード
以下は、1つの取引先(Account)に関連する注文(Order)を1つ作成し、さらにその注文に紐づく2つの注文商品(OrderItem)を同時に作成するための Composite REST API のリクエストボディの例です。このコードは Salesforce Developer Documentation の Composite リソースの例を基に、注文のシナリオに合わせて具体化したものです。
エンドポイント: /services/data/vXX.X/composite
(XX.X は API バージョン)
HTTP メソッド: POST
{ "allOrNone": true, "compositeRequest": [ { "method": "POST", "url": "/services/data/v58.0/sobjects/Order", "referenceId": "newOrder", "body": { "AccountId": "001xx000003DGb2AAG", "EffectiveDate": "2023-11-20", "Status": "Draft", "Pricebook2Id": "01sxx0000002B37AAE" } }, { "method": "POST", "url": "/services/data/v58.0/sobjects/OrderItem", "referenceId": "newOrderItem1", "body": { "OrderId": "@{newOrder.id}", "PricebookEntryId": "01uxx00000061pCAAQ", "Quantity": 5, "UnitPrice": 100.00 } }, { "method": "POST", "url": "/services/data/v58.0/sobjects/OrderItem", "referenceId": "newOrderItem2", "body": { "OrderId": "@{newOrder.id}", "PricebookEntryId": "01uxx00000061pDAAQ", "Quantity": 2, "UnitPrice": 250.00 } } ] }
コードの解説
"allOrNone": true
: このリクエスト全体を一つのトランザクションとして扱います。いずれかのサブリクエストが失敗した場合、すべての操作がロールバックされます。データの整合性を保つために、通常はtrue
に設定することが推奨されます。compositeRequest
: 3つのサブリクエストを含む配列です。- 最初のサブリクエスト (referenceId: "newOrder"):
method
とurl
で、Order オブジェクトに新しいレコードを作成することを指定しています。referenceId
に "newOrder" を設定し、この操作を後続のステップで参照できるようにします。body
には Order レコードの必須項目や関連項目を指定します。AccountId
、EffectiveDate
(契約日)、Status
(状況)、そして注文商品を追加するために必須となるPricebook2Id
(価格表 ID) が含まれています。
- 2番目のサブリクエスト (referenceId: "newOrderItem1"):
- OrderItem オブジェクトにレコードを作成します。
body
の"OrderId": "@{newOrder.id}"
がこのコードの核心部分です。@{newOrder.id}
という構文で、直前のサブリクエストで作成された Order レコードの ID を動的に参照し、関連付けを行っています。PricebookEntryId
(価格表エントリ ID)、Quantity
(数量)、UnitPrice
(単価) は OrderItem の必須項目です。
- 3番目のサプリクエスト (referenceId: "newOrderItem2"):
- 2つ目の OrderItem を作成します。ここでも同様に
"OrderId": "@{newOrder.id}"
を使用して、同じ Order レコードに関連付けています。
- 2つ目の OrderItem を作成します。ここでも同様に
- 最初のサブリクエスト (referenceId: "newOrder"):
このリクエストを Salesforce に送信すると、単一の API コールでこれら3つのレコードが関連付けられた状態で正しく作成されます。
注意事項
Composite REST API は非常に強力ですが、利用する際にはいくつかの注意点を理解しておく必要があります。
権限 (Permissions)
API を実行するユーザーは、対象となるすべてのオブジェクト(この例では Order と OrderItem)に対する「作成」権限を持っている必要があります。また、リクエストボディに含まれるすべての項目(AccountId
, EffectiveDate
, PricebookEntryId
など)に対する項目レベルセキュリティ (Field-Level Security) の参照・編集権限も必要です。
API 制限 (API Limits)
- サブリクエストの上限: 1つの Composite API コールに含めることができるサブリクエストの最大数は 25個です。これを超える数のレコードを一度に作成したい場合は、リクエストを分割する必要があります。
- API コール消費: Composite API リクエスト全体で、Salesforce の API コール制限を 1回だけ消費します。これは、個別に API を呼び出す場合に比べて大幅な節約になります。
- データサイズ制限: リクエスト全体のペイロードサイズにも制限があります。詳細は Salesforce のドキュメントで最新の情報を確認してください。
エラー処理 (Error Handling)
レスポンスは、compositeResponse
というキーを持つ配列で返されます。この配列の各要素が、リクエストの各サブリクエストに対応する結果です。各要素には httpStatusCode
や body
が含まれています。
allOrNone
が true
の場合、いずれかのサブリクエストが失敗すると(例:必須項目漏れ、入力規則違反など)、失敗したサブリクエストのレスポンスにはエラー詳細が含まれ、ステータスコードは 4xx となります。そして、成功したように見えた他のサブリクエストもすべてロールバックされているため、クライアント側ではリクエスト全体が失敗したと判断し、リトライやユーザーへの通知などの処理を行う必要があります。
allOrNone
が false
の場合は、成功したサブリクエストはコミットされ、失敗したサブリクエストのみがエラーを返します。この場合、データの部分的な作成を許容することになるため、ユースケースを慎重に検討する必要があります。
まとめとベストプラクティス
Salesforce Composite REST API は、関連する複数のレコードを一度の API コールで作成・更新するための、開発者にとって不可欠なツールです。特に注文と注文商品のような親子関係を持つデータの作成において、その真価を発揮します。
主な利点:
- パフォーマンス向上: ネットワークのラウンドトリップを削減し、アプリケーションの応答性を高めます。
- API コール数の節約: 組織の API 制限を効率的に利用できます。
- トランザクションの保証:
allOrNone
を利用することで、データの整合性を容易に担保できます。 - 実装の簡素化: クライアント側の複雑なロジックを削減できます。
ベストプラクティス:
- データ整合性を優先する: 親子レコードの作成など、アトミックな操作が求められる場合は、常に
allOrNone: true
を使用してください。 - ペイロードを検証する: API リクエストを送信する前に、クライアント側で必須項目やデータ形式の基本的なバリデーションを行うことで、不要な API エラーを減らすことができます。
- レスポンスを丁寧に処理する: レスポンスボディを適切にパースし、各サブリクエストの結果をチェックする堅牢なエラーハンドリングを実装してください。
- 上限を意識する: 一度に処理するサブリクエストの数が25の上限に近づく場合は、パフォーマンスやデバッグのしやすさを考慮し、リクエストの分割を検討してください。
この強力な API をマスターすることで、Salesforce 開発者はよりクリーンで、効率的で、信頼性の高いインテグレーションを構築することが可能になります。
コメント
コメントを投稿