🐙

【API Gateway・StepFunctions】API呼び出しドメイン(Origin)によってStepFunctions内で分岐をする

に公開

はじめに

以下の構成のように、「APIをたたくドメインによって、後続の処理を変えたい」という実装を検証したので、その方法をご紹介します。

実装方法

実装方法自体は、以下のようにいくつか選択肢があります。

  1. Lambda内でOriginヘッダーをチェック
  2. API GatewayマッピングテンプレートでOriginチェック
  3. フロントエンド側で呼び出し時にカスタムヘッダー追加

今回は、なるべくインフラ側で処理ができるようにしたいので、2番で実装していきます。

APIGateway

Step Functionsへの統合の仕方は割愛します。

APIを作成したら、統合リクエストマッピングテンプレートを編集していきます。

内容としては、リクエストヘッダー内のOriginの内容によって、StepFunctionsに渡すinputskipFirstLambda:trueを付与して渡しています。

StepFunctionsではこのskipFirstLambdaの値を参照し、分岐をします。

VTL
#set($origin = $input.params('Origin'))
#set($escapedBody = $util.escapeJavaScript($input.body))
#if($origin == "https://example.com")
{
  "stateMachineArn": "arn:aws:states:ap-northeast-1:●●●●●:stateMachine::●●●●●:",
  "input": "{\"skipFirstLambda\": true, \"origin\": \"$origin\", \"body\": $escapedBody}"
}
#else
{
  "stateMachineArn": "arn:aws:states:ap-northeast-1::●●●●●::stateMachine::●●●●●:", 
    "input": "{\"skipFirstLambda\": false, \"origin\": \"$origin\", \"body\": $escapedBody}"
}
#end

テスト

では、テストしてみましょう。

ヘッダーにOrigin:https://example.comを入力してテストを押します。
skipFirstLambdatrueになってますね。

Origin:https://stg.example.comでテストすると問題なく、falseになります。

Step Functions

続いてStep Functionsの設定をします。

以下のようなステートマシンを作成します。

JSONat
{
  "Comment": "A description of my state machine",
  "StartAt": "Choice",
  "States": {
    "Choice": {
      "Type": "Choice",
      "Choices": [
        {
          "Next": "Pass",
          "Condition": "{% $states.input.`skipFirstLambda` = true %}"
        }
      ],
      "Default": "Lambda Invoke-1"
    },
    "Pass": {
      "Type": "Pass",
      "Next": "Lambda Invoke2"
    },
    "Lambda Invoke2": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Output": "{% $states.result.Payload %}",
      "Arguments": {
        "FunctionName": "arn:aws:lambda:ap-northeast-1:●●●●●:function:test-lambda:$LATEST",
        "Payload": "{% $states.input %}"
      },
      "Retry": [
        {
          "ErrorEquals": [
            "Lambda.ServiceException",
            "Lambda.AWSLambdaException",
            "Lambda.SdkClientException",
            "Lambda.TooManyRequestsException"
          ],
          "IntervalSeconds": 1,
          "MaxAttempts": 3,
          "BackoffRate": 2,
          "JitterStrategy": "FULL"
        }
      ],
      "End": true
    },
    "Lambda Invoke-1": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Output": "{% $states.result.Payload %}",
      "Arguments": {
        "FunctionName": "arn:aws:lambda:ap-northeast-1::●●●●●::function:test-lambda:$LATEST",
        "Payload": "{% $states.input %}"
      },
      "Retry": [
        {
          "ErrorEquals": [
            "Lambda.ServiceException",
            "Lambda.AWSLambdaException",
            "Lambda.SdkClientException",
            "Lambda.TooManyRequestsException"
          ],
          "IntervalSeconds": 1,
          "MaxAttempts": 3,
          "BackoffRate": 2,
          "JitterStrategy": "FULL"
        }
      ],
      "Next": "Lambda Invoke2"
    }
  },
  "QueryLanguage": "JSONata"
}

テスト

ではこちらもテストしていきましょう。

{
  "skipFirstLambda":true
}

しっかり分岐されていますね。

falseも試してみます。
問題なさそうです。

フロント実装

各ドメインのページからテストHTMLを作成し、リクエストのテストをしてみましょう。
サンプルHTMLは以下です。

API GatewayでCORSの設定を忘れずにしておきましょう。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>API テスト</title>
</head>
<body>
    <button onclick="callAPI()">API呼び出し</button>
    
    <div id="result"></div>

    <script>
        async function callAPI() {
            try {
                const response = await fetch('APIのエンドポイント', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({test: 'test'})
                });
                
                const data = await response.text();
                document.getElementById('result').innerHTML = `<pre>Status: ${response.status}\n${data}</pre>`;
                
            } catch (error) {
                document.getElementById('result').innerHTML = `<pre>Error: ${error}</pre>`;
            }
        }
    </script>
</body>
</html>

おわりに

今回はドメインによってAPIGatewayのマッピングテンプレートを使用して、分岐をし、Step Functionsに渡すデータを整形する方法を検証してきました。
リクエストヘッダーのOriginを使えば、簡単に実装できました。
Lambdaやフロント側でコードを書かずに済むので、リソースの役割を明確に分けることができ、管理もしやすくなるのではないでしょうか。

GitHubで編集を提案

Discussion