画面フローを使ってHTTPコールアウトを実装してみよう

HTTPコールアウトについて、フローを利用した2つの実装方法を紹介します。

はじめに

みなさま、こんにちは!

最近、プロジェクトで画面フローを使ったHTTPコールアウトを実装する機会がありました。
HTTPコールアウトとは、Salesforceから外部のシステムやサービスに対してHTTPリクエストを送信し、そのレスポンスを受け取る機能です。例えば、Salesforce上で入力された郵便番号をもとに外部サービスから詳細な住所情報を取得して画面に表示する、商品IDをもとに外部の在庫管理システムから在庫数を取得してレコードを更新する、等の利用が考えられます。

そんなHTTPコールアウトについてフローを使って実装する方法を調べてみると、SalesforceではフローのHTTPコールアウトアクションを使う方法と、フローからApex(HTTPクラスを使用)を呼び出す方法があることを知りました。
Salesforceで外部連携を検討している方の参考になればと思い、今回はこの2つの実装方法をご紹介いたします。

HTTPコールアウトの実装

今回は、下記のシナリオに沿って、

  1. ① フロー単独 ― HTTPコールアウトアクションを使用 ―
  2. ② フロー+Apex ― HTTPクラスを使用 ―
の順に実装していきます。

【シナリオ】
取引先レコード画面に配置した画面フローからサンプルAPI(※1)を呼び出し、レスポンスを取得して画面に表示する

(※1)サンプルAPI:
今回はJSONPlaceholderというテストやプロトタイピング用に提供されている無料のREST APIを利用します。

まず、①の実装です。

①フロー単独 ― HTTPコールアウトアクションを使用 ―


〈実装手順〉
  1. 外部ログイン情報とプリンシパル、指定ログイン情報の設定
  2. 権限セットの作成・付与
  3. フロー:HTTPコールアウトアクションの作成
  4. フロー:HTTPコールアウトアクションの起動~結果表示の設定
1.外部ログイン情報とプリンシパル、指定ログイン情報の設定
外部ログイン情報では、認証プロトコル(※2)を使用した外部システムに対するSalesforceの認証方法を設定します。今回はサンプルAPIを使用するため、認証プロトコルは「認証なし」とします。

次に、プリンシパル(※3)を作成します。手順2でフローの実行ユーザーに権限を付与する際に、作成したプリンシパルを権限セットに対応付ける必要があります。

(※2)認証プロトコル:
システムやアプリケーションへのアクセスを許可する前に、ユーザーの身元を確認するための手順や規約
(※3)プリンシパル:
認証されたユーザーやシステム

続いて、指定ログイン情報からコールアウトエンドポイントURLを設定し、認証方法として先程設定した外部ログイン情報を対応付けます。

2.権限セットの作成・付与
権限セットに、手順1で作成したプリンシパルに対するアクセス権を追加し、フローの実行ユーザーにこの権限セットを割り当てます。

3.フロー:HTTPコールアウトアクションの作成
フローのアクション要素として、HTTPコールアウトを実装できる標準機能が用意されています。アクション要素を選択し、下部の「HTTPコールアウトを作成」を押下します。

外部サービス名を入力し、手順1で設定した指定ログイン情報を選択します。

コールアウトで実行するメソッドとして、データを取得するGETを選択します。

今回は「スキーマ用に接続」を選択します。

「接続」ボタンを押下すると、実際にサンプルAPIに接続しGETメソッドで取得できるレスポンスのデータ構造が右側に表示されます。保存と同時に、外部サービスが作成されます。
HTTPコールアウトアクションの作成はこれで完了です。

4.フロー:HTTPコールアウトアクションの起動~結果表示の設定
API連携開始画面を作成し「次へ」ボタンを押下した時、手順3で作成したHTTPコールアウトアクションを起動します。
HTTPコールアウトアクションの結果、レスポンスを格納した出力パラメータが返却されます。

決定要素を用いて、Response Codeが200(コールアウトが成功)の場合は成功画面に返却されたレスポンス結果を、Response Codeが200以外の場合は失敗画面にそのエラー内容を表示するよう設定し、①の実装は完成です!



次に、②の実装です。

②フロー+Apex ― HTTPクラスを使用 ―


〈実装手順〉
  1. リモートサイトの設定
  2. Apexクラスの作成
  3. フロー:Apexアクションの作成
  4. フロー:Apexアクションの起動~結果表示の設定
1.リモートサイトの設定
コールアウトエンドポイントURLを設定します。

2.Apexクラスの作成
次の2つのApexクラスを作成します。
  • HTTPコールアウト呼び出し用クラス(ApiIntegration.cls):
    HTTPコールアウト処理用クラス(ApiService.cls)を起動し、受け取ったレスポンスをフローに返却します。
  • HTTPコールアウト処理用クラス(ApiService.cls):
    Salesforceで用意されているHTTPクラスを使用して、サンプルAPIからレスポンスを取得します。
public class ApiIntegration {

    // 入力パラメータ(画面フロー→Apex)
    public class FlowInput {
        @InvocableVariable (required = true)
        public String accountId;
    }

    // 出力パラメータ(Apex→画面フロー)
    public class FlowOutput {
        // API連携成功フラグ
        @InvocableVariable
        public Boolean isSuccess;

        // 画面フロー上に表示するレスポンス値
        @InvocableVariable
        public Integer userId;
        @InvocableVariable
        public Integer id;
        @InvocableVariable
        public String title;
        @InvocableVariable
        public Boolean completed;

        // 失敗した時、画面に表示するメッセージ
        @InvocableVariable
        public String message;
    }

    @InvocableMethod (label = 'ApiIntegration')
    public static List<FlowOutput> apiIntegration(List<FlowInput> flowInputs) {
        // フロー上での出力パラメータ用リストを準備
        List<FlowOutput> results = new List<FlowOutput>();
        FlowOutput result = new FlowOutput();

        try {
            // レスポンスを受け取るための内部クラスのインスタンスを生成
            MyJsonData apiResponseData = new MyJsonData();

            // 入力パラメータから1つ目の取引先レコードIdを取得
            String accountId = flowInputs[0].accountId;

            // APIコールアウトを実行
            String responseBody = ApiService.callPublicApi();
            
            // JSONレスポンスの分解とオブジェクトへのマッピング
            apiResponseData = deserializeJSON(responseBody);

            // 分解した結果を出力パラメータに設定
            if (apiResponseData != null) {
                result.isSuccess = true;
                result.userId = apiResponseData.userId;
                result.id = apiResponseData.id;
                result.title = apiResponseData.title;
                result.completed = apiResponseData.completed;
            } else {
                result.isSuccess = false;
                result.message = 'APIレスポンスの分解に失敗しました。';
            }

        } catch (Exception e) {
            result.isSuccess = false;
            result.message = 'API連携中に予期せぬエラーが発生しました:' + e.getMessage();
        }

        results.add(result); // 処理結果をリストに追加して返す
        return results;
    }

    // JSONの構造に対応する内部クラスを定義
    private class MyJsonData {
        public Integer userId;
        public Integer id;
        public String title;
        public Boolean completed;
    }

    // JSON列分解メソッド
    private static MyJsonData deserializeJSON(String responseBody) {
        try {
            return (MyJsonData) JSON.deserializeStrict(responseBody, MyJsonData.class);
        } catch (JSONException e) {
            System.debug('JSONデシリアライズエラー: ' + e.getMessage());
            return null;
        }
    }
    
}
ApiIntegration.cls
public class ApiService {

    public static String callPublicApi() {
        // HttpRequestオブジェクトを作成
        HttpRequest req = new HttpRequest();

        // エンドポイントURLを設定 (今回はJSONPlaceholderのサンプルAPIを使用)
        req.setEndpoint('https://jsonplaceholder.typicode.com/todos/1');
        // HTTPメソッドを設定 (GET)
        req.setMethod('GET');
        // タイムアウトをミリ秒で設定 (例: 10秒)
        req.setTimeout(10000);

        // Httpオブジェクトを作成
        Http http = new Http();
        HttpResponse res;

        try {
            // APIにリクエストを送信し、レスポンスを取得
            res = http.send(req);
            // レスポンスのステータスコードを確認
            if (res.getStatusCode() == 200) {
                // 成功した場合、レスポンスのボディ(JSON文字列)を返す
                return res.getBody();                
            } else {
                // エラーの場合、ステータスコードとメッセージを返す
                return 'callPublicApi Error: API request failed with status code ' + res.getStatusCode() + ' - ' + res.getStatus();
            }
        } catch (System.CalloutException e) {
            // コールアウト例外(接続できない、タイムアウトなど)をキャッチ
            // 呼び出し元で処理できるよう、例外を再スローするか、エラーメッセージを返す
            throw new AuraHandledException('APIへの接続に失敗しました: ' + e.getMessage());
        }
    }
    
}
ApiService.cls
3.フロー:Apexアクションの作成
フローのアクション要素から、手順2で作成したHTTPコールアウト呼び出し用クラス(ApiIntegration.cls)を選択します。

API連携開始画面を作成し「次へ」ボタンを押下した時、このApexアクションを起動します。
HTTPコールアウト呼び出し用クラス(ApiIntegration.cls)で入力パラメータとして定義したaccountIdにrecordIdを渡します。また、その下部には出力パラメータとして定義した変数が一覧で表示されており、Apexで処理された結果が格納されてフローに返却されます。

4.フロー:コールアウトの結果表示の設定
決定要素を用いて、API連携成功フラグ(isSuccess)がTrueの場合は成功画面に返却されたレスポンス結果を、API連携成功フラグがFalseの場合は失敗画面にそのエラー内容を表示するよう設定し、②の実装は完成です!

動作確認

以上で、

  1. ① フロー単独 ― HTTPコールアウトアクションを使用 ―
  2. ② フロー+Apex ― HTTPクラスを使用 ―
の実装が完了しました!

では、実際に動かしてみましょう。

取引先レコード画面に配置した画面フローの「次へ」ボタンを押下します。

どちらの場合でも同様に、HTTPコールアウトが実行でき、レスポンスを受け取って画面に表示することができました!

最後に

今回はHTTPコールアウトを実装する2つの方法についてご紹介しましたが、いかがだったでしょうか。
フロー単独ではレスポンスの分解メソッドの記述が不要で手間が省けましたが、レスポンスに対してエラーチェック等の複雑な処理を加える場合はApexでの実装が必要に感じました。

少しでも参考になりましたら幸いです。最後までご覧いただきありがとうございました!

参考サイト