のべラボ.blog

Tech Blog | AWS や 生成 AI 、サーバーレス、コンテナ などなど

チャットのストリーム表示における Amazon API Gateway の WebSocket API の活用

本記事は「Serverless Advent Calendar 2025」7 日目の記事です。


チャットアプリケーションのストリーミング表示の方法について

基盤モデルを使って Web ベースのチャットアプリケーションを作成するケースはよく見受けられますよね。 私も基盤モデルを使用するアプリケーションのデモやサンプルとして、よく作ります。

その際、「レスポンスのメッセージのストリーミングで表示したい」 というニーズも多いと思います。

世の中には色々な SDK がありますが、基盤モデルを直接呼び出す API でも、AI エージェントをコントロールする API でも、ストリーミングでレスポンスを得る API は、だいたい用意されています。

なので、それらを使えばいい、ということになるのですが、チャットアプリケーションの構造によって適用できる技術やフレームワーク、ランタイムが変わってきます。

例えば、Streamlit を使用し、そのコードの中でストリーミングのレスポンスを得る API を使用して実装し、コンテナにしてサーバーサイドで動作させるだけなら、至極シンプルで、何の問題もありません。

では、サーバーレスのコンピューティング環境として AWS Lambda を使いたい場合はどうでしょうか?

AWS Lambda を単体で使うなら、関数 URL を活用することもできます。

docs.aws.amazon.com

ただ、Amazon API GatewayAWS Lambda を組み合わせた構成の場合はどうでしょうか?

そういった場合に役立つ Amazon API Gateway の Update が 2025年11月 に発表されました。

Amazon API GatewayREST APIAWS Lambda 関数と統合した場合のレスポンスストリームをサポート」

aws.amazon.com

これは とてもよい Update だと思いますし、すでに多くの人に試され、その感触などが様々なブログ記事で公開されています。

ただ、AWS Lambda 関数と組み合わせる場合は、AWS Lambda の InvokeWithResponseStream API を使うことになり、ランタイムは Node.js が前提です。

docs.aws.amazon.com

docs.aws.amazon.com

もし、Node.js 以外のランタイムを使いたい場合は、Web Adapter の使用などを検討することになります。

では、AWS Lambda の関数 ランタイムとして Node.js 以外を使いたい、 Web Adapter を使いたくない、という場合はどうでしょうか?

その場合は、Amazon API Gateway の WebSocket APIAWS Lambda 関数を組み合わせる という方法もあります。

今回は、この方法を試してみたいと思います。


今回作成するアプリケーションの構成

  • Amazon API Gateway で sendtext というハンドラを用意して、AWS Lambda 関数と統合します。
  • AWS Lambda 関数は、受け取ったメッセージを Amazon Bedrock に対して converse_stream という API で送信します。
    • この AWS Lambda 関数は関数 URL や WebAdapter を使う必要はなく、ランタイムは Python 3.13 を使用しています。
  • そのレスポンスをストリームで受け取り、Amazon API Gateway へ返信します。
  • フロントエンドでは、Amazon API Gateway の WebSocket API のエンドポイントの URL に対して WebSocket の接続を維持して、sendtext のハンドラが実行されるようにメッセージを送信し、レスポンスを受信します。

フロントエンドに Streamlit を使うことも検討したのですが、WebSocket の接続を維持するためのコードが複雑になったので、Next.js での実装に切り替えました。


AWS Lambda 関数

  • sendtxt ハンドラとなる AWS Lambda 関数では、Amazon Bedrock の基盤モデルに対して converse_stream API でメッセージを送信します。
  • 基盤モデルからのストリームレスポンスは、Amazon API Gateway に対して post_to_connection API でメッセージを返信します。
import boto3
import json
import os

API_ENDPOINT = os.environ["API_ENDPOINT"]
STAGE = os.environ["STAGE"]
MODEL_ID = os.environ["MODEL_ID"]

# 推論パラメータの値
temperature = 0.5

# 推論パラメータの値の設定
inference_config = {"temperature": temperature}


# システムプロンプト
system_prompts = [{"text": "あなたは優秀なアシスタントです。問い合わせ内容に丁寧に応答して下さい。"}]

def lambda_handler(event,context):
    # AWS SDK のクライアントオブジェクトの作成
    brt = boto3.client(service_name='bedrock-runtime')
    apigw_management = boto3.client('apigatewaymanagementapi', endpoint_url=f"{API_ENDPOINT}/{STAGE}")

    # 接続 ID の取得
    connectionId = event.get('requestContext', {}).get('connectionId')

    # メッセージの取得
    body_content = json.loads(event.get('body', {}))

    # 基盤モデルへのリクエストメッセージの構成
    text = body_content.get('text')
    message_1 = {
      "role": "user",
      "content": [{"text": f"{text}"}]
    }
    messages = []

    messages.append(message_1)
    
    # Bedrock の基盤モデルの ID を指定
    modelId = MODEL_ID

    # Bedrock の基盤モデルへリクエストを送信
    try:
        response = brt.converse_stream(modelId=modelId,messages=messages,system=system_prompts,inferenceConfig=inference_config)
    except Exception as e:
        return {
            "statusCode": 500,
            "body": f"{e}"
        }
    # レスポンスを取得し、Amazon API Gateway へ返信
    for event in response.get('stream'):
         if 'contentBlockDelta' in event:
            chunk = event['contentBlockDelta']['delta']['text']
            try:
                apigw_management.post_to_connection(ConnectionId=connectionId, Data=json.dumps(chunk))
            except Exception as e:
                return {
                    "statusCode": 500,
                    "body": f"{e}"
                }

    return {
        "statusCode": 200
    }

この AWS Lambda 関数と Amazon API Gateway の WebSocket API と、その統合を作成する AWS SAM テンプレートはこちらの GitHub リポジトリ から参照できです。


フロントエンド

ストリーム表示に関わる部分だけフォーカスして説明します。

下記では、useEffect で WebSocket の接続を作成・維持を行っています。また、ws.onmessage でメッセージを細切れに受け取り、useState で管理している messages へ格納しています。

export default function Chat() {
  const [messages, setMessages] = useState<Message[]>([]);
  const [input, setInput] = useState("");
  const [isConnected, setIsConnected] = useState(false);
  const wsRef = useRef<WebSocket | null>(null);
  const currentResponseRef = useRef<string>("");
  const currentMessageIdRef = useRef<number | null>(null);

  useEffect(() => {
    const ws = new WebSocket(process.env.NEXT_PUBLIC_WebSocket_URL!);
    wsRef.current = ws;

    ws.onopen = () => setIsConnected(true);
    ws.onclose = () => setIsConnected(false);
    ws.onerror = () => setIsConnected(false);
    
    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      const messageText = String(data);
      
      currentResponseRef.current += messageText;
      
      if (currentMessageIdRef.current) {
        setMessages(prev => 
          prev.map(msg => 
            msg.id === currentMessageIdRef.current 
              ? { ...msg, text: currentResponseRef.current }
              : msg
          )
        );
      }
    };

    return () => ws.close();
  }, []);

下記は WebSocket でユーザーのメッセージを送信しています。

 wsRef.current.send(JSON.stringify({
      action: "sendtext",
      text: promptWithHistory
    }));

下記は useState で管理している messages を表示している部分です。

<div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map((message) => (
          <div
            key={message.id}
            className={`flex ${message.isUser ? "justify-start" : "justify-end"}`}
          >
            <div
              className={`max-w-md px-4 py-2 rounded-lg break-words ${
                message.isUser
                  ? "bg-yellow-200 text-black"
                  : "bg-green-200 text-black"
              }`}
            >
              {message.text}
            </div>
          </div>
        ))}
      </div>

上記以外のフロントエンドのプロジェクトのリソースは こちらのGitHub のリポジトリ にまとめています。 フロントエンドのコードは、Amazon Q Developer の力を借りて作成したので、1時間ほどで作成できました。


完成イメージ


注意点

Amazon API Gateway の WebSocket API は、リクエスト数だけでなく接続時間にも課金されます。 これは REST API とは異なりますので注意しましょう。

aws.amazon.com


最後に

基盤モデルを使ったチャットアプリケーションのようにレスポンスをストリーム表示するような場合で AWS のサーバーレスのサービスを使う場合は、いくつか方法はありますが、Amazon API Gateway の WebSocket API を活用することで、統合する AWS Lambda 関数のコードをシンプルにできるというメリットを感じました。

もちろん、2025年11月に Update された Amazon API GatewayREST API のレスポンスストリーム対応も役立ちますが、WebSocket API の場合は、AWS Lambda 関数のランタイムを Node.js にする必要がなく、Web Adapter も不要であることはメリットといえると思います。

WebSocket API 料金は意識しておく必要はありますが、サーバーレスでの実装パターンの一つとして今後も活用していきたいと思います。


非ベンダー主催の生成 AI 認定 3冠 になったので振返ってみた

生成 AI の認定資格には色々ありますよね。

AWSGoogleMicrosoft などのベンダーが実施している試験もあります。

私も AWS の認定は取得しましたが、一方でベンダーが絡まず、純粋に基礎から体系的に生成 AI 関連のスキルを確認・測定できる認定試験にも強い関心をもっていました。

そこで、国内の下記の生成 AI に関する3つの認定を取得したので、その内容を振り返ってみたいと思います。

なお、このブログ記事の内容は 2025 年 6 月時点の情報に基づいています。 試験の情報は更新されることもありますので、ご注意ください。

上記 3つはいずれも生成 AI の初学者の方でもチャレンジできる認定試験ですので、興味があれば以下の記載を参考にしてください。


生成 AI パスポート

生成 AI パスポートは、一般社団法人生成AI活用普及協会 (GUGA) が実施している試験です。 問題形式は選択式で、自宅でオンラインで受験できます。 私は 2024 年 6 月に受験して合格しました。

問題形式 問題数 試験時間 合格基準
選択式 60 60分 開示していない

以下、生成 AI パスポートを受験した所感です。

  • 適度なシラバスの範囲

    • 生成 AI について基礎から体系的に学びその知識を試す、という私の目的を満たしてくれる試験の一つであったといえます。 シラバスの範囲としても、AI や 生成AIの基礎知識、現状の動向、実践的な活用方法、活用時の注意点など、基本的に理解しておきたいトピックを網羅できています。
    • 生成 AI を構築するための深層学習の手法やアルゴリズムの種類についても問われます。深いレベルでは問われないので恐れる必要はありませんが、初心者の方にはこの分野の聞きなれない言葉や用語の暗記に一苦労するかもしれません。
  • 試験の学習教材が豊富

    • 一般書籍として、試験の参考書、問題集が発刊されています。
    • 無料の生成 AI パスポート AIクイズアプリをスマホから利用できます。
    • 私はこれらの教材を2周ほど学習して試験に臨み、合格に役立ったと言えます。
  • 試験の実施期間に注意
    • 試験はいつでも受験できるわけではなく、実施期間は限られていますので注意しましょう。
    • 2025年の場合は、2月、6月、10月に実施されます。
    • 合否は、受験後しばらく経ってからの発表になります。私は 2024年に受験しましたが、一か月後にようやく合格していることを確認できました。

合否発表まで待っている期間はヤキモキしますが、3つの認定試験の中でシラバス範囲はバランスがよく、かつ教材も豊富で学習しやすいという印象を持ちました。


Prompt Engineering Professional - PEP

Prompt Engineering Professional - PEP は、一般社団法人日本プロンプトエンジニアリング協会が実施している試験です。 問題形式は選択式で、テストセンターでの受験となります。 私は 2025 年 5 月に受験して合格しました。

問題形式 問題数 試験時間 合格基準
選択式 100 60分 70%以上の正解

以下、Prompt Engineering Professional - PEP を受験した所感です。

  • 生成 AI の活用にフォーカスしている
    • 私は 生成 AI パスポートを合格した後にこの試験を受験したのですが、生成 AI パスポートとシラバス範囲が似ている部分もあるので、以前に学習した知識を活用できました。
    • 試験名に「Prompt Engineering」という言葉が含まれている通り、プロンプトエンジニアリングについても問われる問題が一定量出題されます。また ChatGPT など、一般的によく使用されている生成 AI のサービスの活用についても問われる問題も出題されるので、"生成 AI の実際の活用" にフォーカスしている印象を受けました。
  • 公式の学習資料が役立つ
    • この記事の執筆時点では試験の参考書や問題集は一般書籍として発刊されていませんが、シラバスに沿った学習資料が公式サイトから入手できます。
    • この学習資料が、試験に役立ちます。私もこれらの資料を2~3周ほど学習して試験に臨み合格できました。
  • 試験日時は選択可能だがテストセンターで受験
    • 試験はテストセンターまで赴く必要はありますが、私は AWS の認定試験で慣れているので特に苦になりませんでした。
    • 合否結果は、受験後にすぐに提示してくれるのも、個人的にありがたいです。

シラバスに沿った学習資料が公開されていることや、私は生成 AI パスポート合格後にこの試験を受けたことあり、この試験は個人的には難易度はあまり高くないと感じました。深層学習などの知識よりも、生成 AI を実際に活用する部分にフォーカスしている印象なので、「今、生成 AI をよく使っている」という方には取り組みやすい試験ではないかと感じました。


Gen AI Test

Gen AI Test は、一般社団法人日本ディープラーニング協会が実施している試験です。 問題形式は選択式 (19 問) と記述式(1 問)で、自宅でオンラインで受験できます。 私は 2025 年 6 月に受験して合格しました。

問題形式 問題数 試験時間 合格基準
選択式 19問と記述式 1問 20 20分 非公開

以下、Gen AI Test を受験した所感です。

  • シラバス範囲、問題量ともに多くない
    • Gen AI Test は問題数が 20 問で、シラバス範囲も他の2つの試験と比較し多くはありません。
    • そういう意味では、チャレンジのハードルが高くないともいえます。
  • 試験対策の教材は多くない
    • 例題・過去問の一部や試験に関して説明する動画や、参考資料は公開されており、それはそれで役に立ちますが他の2つの試験と比較して教材は多いとはいえません。
    • ただし試験を実施している日本ディープラーニング協会は、G 検定を実施している団体でもあり、生成 AI や深層学習に関連する用語の解説は G 検定向けの情報や教材をみることでカバーできます。
  • 試験の実施期間に注意
    • 試験はいつでも受験できるわけではなく、実施期間は限られていますので注意しましょう。
    • 2025 年の場合は、6 月に 1 回目が実施されました。2 回目についてはまだ情報が公開されていません。ただ、2024 年度の実績をみると 2 回実施されていますので今後の開催の情報を確認してみてください。

この試験は他の2つと比較し、問題数が少ないので「勉強したぞ!そして合格できたぞ!」という感覚を得られにくいかもしれません。ただ他の2つの試験と異なり、記述問題が出題されます。この記述問題は自分の実力を測るのには適していると感じています。選択式の問題と異なり、記述問題の場合は本当に問題の意味を理解していないと回答を記述することができません。記述問題の出題は 1 問だけではありますが、真の実力を測定するという意味では、良い経験ができたと感じています。


以上です。 今後、上記の認定取得に興味がある方は参考にしていただければ幸いです。

生成 AI は、昨今非常に注目されている分野ですので、今後また新しい認定試験が出るかもしれませんね。 その場合はまたチャレンジしようと思います!


Amazon Bedrock ナレッジベースの Web クローラーを触ってみた

本記事は「Amazon Bedrock Advent Calendar 2024」22 日目の記事です。

re:Invent 2024 での Update と関連のないトピックですみませんmm



Amazon Bedrock ナレッジベース のデータソースについて

Amazon Bedrock ナレッジベースは、検索拡張生成 (RAG) を使用した生成 AI アプリケーションを構築するのに役立ちます。

docs.aws.amazon.com

通常、ベクトルデータベースを使用した RAG の仕組みを構築する場合は、次のような処理を実装する必要があります。

事前処理

  1. データソースからドキュメントを取り出す
  2. 埋め込みモデルでドキュメント内のテキストのベクトル値を取得する
  3. ベクトル値をデータベース(ベクトルデータベース)に保存する

(下図は例です)

推論実行時の処理

  1. 埋め込みモデルでプロンプトのテキストのベクトル値を取得する
  2. ベクトルデータベースからプロンプトのベクトル値と類似するドキュメントを取得する
  3. 取得した類似ドキュメントをコンテキストとしてプロンプトに含め、テキスト生成モデルに推論を実行する

(下図は例です)

Amazon Bedrock ナレッジベースの場合は、AWS マネジメントコンソールを使用すれば上記の事前処理を設定作業だけで実現できます。 また推論実行時の処理もナレッジベースが提供する API を使用するだけで内部的に実行してくれるので、アプリケーション側で 上記の 1 から 3 の処理を実装する必要がありません。

下図は Amazon Bedrock ナレッジベースを使用した場合の処理の流れです。 流れが複雑にみえるかもしれませんが、ナレッジベースを作成しておけば、アプリケーションからはその API を呼び出すだけでよいという部分に着目して下さい。

(下図は データソースとして Amazon S3 バケットを使用している例です)

このナレッジベースでデータソースとして使用できるのは、この記事を執筆している 2024年12月時点では下記となります。

  • ベクトルストアを使用する場合のデータソース
  • Amazon Kendra を使用する場合のデータソース
  • 構造化データストアを使用する場合のデータソース

ただし、ベクトルストアを使用する場合のデータソースとしては、上記以外で、プレビュー として Web クローラーやConfluence、SalesforceSharepoint も試すことができます。

この記事では、このうち Web クローラーを試してみた結果を紹介したいと思います。

ただし、2024年12月現在 Web クローラーはプレビューリリースであり、今後変更される可能性があることはご注意下さい。


Web クローラーをデータソースに指定した場合のイメージ

Web クローラーをデータソースに指定するということは、推論実行時にコンテキストとして参照させたいドキュメントは、Web サイトに存在するという前提になります。

今回、Web クローラーを試すにあたって、デモ用の Web サイトを用意しました。これは、AnyCompany という架空の会社の最新情報を発しするニュースリリースサイトという想定です。(内容が プアなのはご容赦下さいw)  このサイトには、AnyCompany 社の決算説明資料として PDF ファイルもリンクされています。

AnyCompany (架空の会社)のニュースリリースサイト


AWS マネジメントコンソールでの設定の流れ

まず、Amazon Bedrock のコンソールの左側ナビゲーションメニューから [オーケストレーション] - [ナレッジベース] を選択します。右側に表示される [ナレッジベースの作成] を選択し、[Knowledge Base with vector store] を選択します。

ナレッジベースの名前や説明を入力します。IAM ロールは今回は [新しいサービスロールを作成して使用] を選択しました。

次にデータソースとして [Web Crawler - Preview] を選択します。

それ以外は今回はデフォルトのまま、[次へ] を選択します。

次にデータソース名や クロール対象となる URL を設定します。

その下に [同期スコープ] というセクションがあり、クロール対象とするドメイン範囲や、対象ファイルの Regex パターンなどを設定できますが、今回はデフォルトのままとしました。

例えばドメイン範囲では、次のようなオプションを選択できます。

オプション 概要
Default 同じホストに属し、同じ初期 URL パスを持つ Web ページにクロールを制限します。たとえば、シード URL が「https://aws.amazon.com/bedrock/」の場合、このパスと、このパスから拡張された Web ページ (「https://aws.amazon.com/bedrock/agents/」など) のみがクロールされます。たとえば、「https://aws.amazon.com/ec2/」などの兄弟 URL はクロールされません。
Host only クロール対象を同じホストに属する Web ページに制限します。たとえば、シード URL が「https://aws.amazon.com/bedrock/」の場合、「https://docs.aws.amazon.com」を含む Web ページ (「https://aws.amazon.com/ec2」など) もクロールされます。
Subdomains シード URL と同じプライマリ ドメインを持つすべての Web ページのクロールを含めます。たとえば、シード URL が「https://aws.amazon.com/bedrock/」の場合、「https://www.amazon.com」のように「amazon.com」を含むすべての Web ページがクロールされます。

それ以外は今回はデフォルトのまま、[次へ] を選択します。

次に埋め込みモデルを選択します。今回は多言語に対応していることやコストなども考慮し、Amazon Titan Text Embeddings v2 を選択しました。

さらにベクトル値を格納するベクトルデータベースを選択します。

ここは注意が必要です。 Web クローラーをデータソースに選択した場合はベクトルデータベースとして Amazon OpenSearch Serverless を指定する必要があります。 当初、Amazon OpenSearch Serverless 以外をベクトルデータベースとして指定して作成しようとしたのですが、下記のエラーが表示されました。

データソース「kb-anycompany-news-web-ds」をナレッジベースに追加できませんでした。 WEB data source is currently only supported for knowledge bases created with an Amazon OpenSearch Serverless vector database.

そのため今回は、[新しいベクトルストアをクイック作成] を選択しました。ただし、Amazon OpenSearch Serverless を使用するコストには十分ご注意ください。 今回は、動作確認後にナレッジベースも Amazon OpenSearch Serverless も削除することにします。

それ以外は今回はデフォルトのまま、[次へ] を選択し、ナレッジベースを作成します。

作成が完了すると、次のようなメッセージが表示されます。

次に同期処理が必要になるので、[データソース] のセクションで作成したナレッジベースのチェックボックスをチェックして、[同期] を選択します。

同期の時間は対象データの量に依りますが、今回のクロール対象の Webサイトはさほどデータ量は多くないので数分待つと完了し、次のようなメッセージが表示されます。

これでナレッジベースが使えるようになりました。

では引き続きマネジメントコンソールを使用してテストしてみましょう。

ページ右側にある [ナレッジベースをテスト] で 推論に使うテキスト生成モデルを選択します。

その後、「AnyCompany 社の最新情報を教えてください」と問い合わせてみます。

上図は例ですが、Web サイトの情報から回答を出せていることがわかりますね!

回答の中には、[1] や [2] など、参照先を示すリンクもありますが、それらをクリックしても指定した Web サイトや Web サイトにリンクされている PDF ファイルを参照していることがわかります。

これで、Web クローラーが正しく動作していることが確認できました!


最後に

RAG の仕組みを実装する上では、使用したいドキュメントや情報が Amazon S3 バケットではなく、Web サイトに存在している場合もありえます。 Amazon Bedrock のナレッジベースの Web クローラーを今回試してみて、そういったケースではシンプルに使用できて非常に有益であると実感できました。

今回は基本的な機能だけを試してみましたが、下記のドキュメントに詳細が記載されているので、詳細を知りたい方はご参照ください。

docs.aws.amazon.com

ただし、2024年12月時点では Web クローラーはプレビューであることは十分、ご留意ください。

また、不要であればナレッジベースとそのベクトルデータベースは削除しましょう。Amazon OpenSearch Serverless の場合、OpenSearch のコンソールからコレクションやセキュリティの各種ポリシーも削除しておきましょう。

ではみなさん、よいクリスマスを!


AWS Step Functions を使用したサーキットブレーカーパターンの実装について

本記事は「AWS LambdaとServerless Advent Calendar 2024」15 日目の記事です。

以前から下記のブログに関心があったので、実際に試してみたり、自分なりに改良を加えてみたのでそれを記事にします。

aws.amazon.com


目次


サーキットブレーカーとは

まず、サーキットブレーカーとは何かというところ確認しておきます。 アプリケーション開発の文脈でいうと、サーキットブレーカーとはサービス呼び出しが正常に行えない場合に、過剰にリクエストを送信しないための仕組みのことを指し、その仕組みの実装パターンを、サーキットブレーカーパターンと呼んでいます。

サーキットブレーカーパターンは、下記の AWS 規範ガイドにも説明がありますので目を通しておくと理解が深まります。

docs.aws.amazon.com

ちなみに「サーキットブレーカー」という言葉は、アプリケーション開発以外の場面でも使われます。 例えば、電流を制御する物理的な装置もサーキットブレーカーと呼ばれますし、株式の取引でも急激な株価の下降を止めるために取引を停止することをサーキットブレーカーと呼ぶこともあります。

サーキットブレーカーパターンの実装例

このサーキットブレーカーパターンの実装を紹介しているのが上で紹介した Using the circuit breaker pattern with AWS Step Functions and Amazon DynamoDB というブログ記事です。 そのブログ記事のサーキットブレーカーの動作を図にしてみました。

上の図では、サーキットブレーカーが Payment サービスが使用できない状態を「サーキット状態 = OPEN 」として保存しています。 サーキット状態が OPEN であれば、次のリクエストでは Payment サービスは呼び出さずエラーにしています。 ただし、Payment サービスが復旧することに備えて、この状態の保存には期限を設けることを前提としています。 これを実際に実装したイメージが下図になります。(ブログ記事からの抜粋した図です)

https://d2908q01vomqb2.cloudfront.net/1b6453892473a467d07372d45eb05abc2031647a/2022/01/30/Screen-Shot-2022-01-30-at-10.52.11-AM.png

サーキット状態の保存には Amazon DynamoDB テーブルを使用していますが、項目の有効期限 (Time To Live) を設定しておきます。 これにより、Payment サービスが復旧した場合に、サーキット状態=OPEN という項目が残り続けてリクエストを拒否する動作を避けることができます。

実際に試してみましたが、この実装には下記の利点があると感じました。

  • シンプルに実装できる
    • サービスを呼び出す側にサーキットブレーカーの実装を含めなくてよく、サーキットブレーカーを独立させて汎用的に使用できます。
  • サーキットブレーカーの動作や状態を可視化しやすい
    • AWS Step Functions のステートマシンで実装されているので、AWS マネジメントコンソールでその実行を可視化、トレースできますし、サーキット状態も DynamoDB のテーブルの内容をみることですぐに確認できます。

サーキットブレーカーパターンの実装例を自分なりに改良してみる

上記で紹介したサーキットブレーカーパターンの実装は、サンプルとしては十分ですが、実際に使うとなった場合は、下記のようにいくつか機能面で不足していることに気づきました。

  1. Order サービスが、Payment サービスに Payload を渡せる実装になっていない
  2. サーキット状態が OPEN のため呼び出しを行わない場合、Payload を退避させる実装になっていない

上記の点を踏まえて、自分なりに改良を加えることにしました。 またブログ記事では ステートマシンから呼び出す Lambda 関数のランタイムが .NET C# ですが、Python を使う人にも理解しやすいように、Lambda 関数のランタイムを Pythonで実装しなおしました。 そのステートマシンのイメージが下図です。Update Circuit Status ステートの後に Amazon SQS のキューに Payload を送信するステートを追加しました。これにより、呼び出しが失敗した場合でも、その Payload の消失を防げます。

実際のコードや AWS SAM リソースはこちらになります。(あくまで個人のコードでありサンプルとして公開しています。)

では実行してみましょう。まずは呼び出し先の Payment サービスに問題がなく、正常に呼び出せるケースを試してみます。

上の図のように、最初はサーキット状態はクローズで、実際に Payment サービスも問題なく呼び出せるので Succeed ステートである Circuit Closed で終了します。

次は、呼び出し先のサービスに問題があり、正常に呼び出せないケースです。 このケースでは、AWS Lambda 関数で必ずタイムアウトエラーが発生する PaymentTimeout というサービスを呼び出します。 すると、Execute Lambda ステートでエラーが発生しますが、リトライを設定しているので、何度かリトライを行います。 ただし、リトライをしてもエラーになるので、最終的には Fail ステートである Circuit Opened で終了します。

この時、Amazon DynamoDB テーブルへ下記の項目を Put します。

ServiceName
(Partition Key)
ExpireTimeStamp
(Sort Key)
CircuitStatus
Payment 現在のエポック秒へ 20 秒足した値 OPEN

またサービス呼び出し元から送信された Payload や Lambda 関数のエラーメッセージ(下記)を Amazon SQS のキューへ送信しています。

{
    "executionId": "arn:aws:states:ap-northeast-1:000000000000:execution:circuitbreaker-statemachine:5464ba09-3c01-4f8a-9da3-8007d67225ed",
    "state": {
        "TargetLambda": "arn:aws:lambda:ap-northeast-1:000000000000:function:circuitbreaker-PaymentTimeout",
        "Payload": {
            "order": {
                "item_id": "Dummy02",
                "unit": 20
            }
        },
        "output": {
            "TargetLambda": "arn:aws:lambda:ap-northeast-1:000000000000:function:circuitbreaker-PaymentTimeout",
            "CircuitStatus": ""
        },
        "taskresult": {
            "Error": "States.Timeout",
            "Cause": ""
        }
    }
}

これでサーキット状態が OPEN になりました。 この状態は DynamoDB テーブルへ保存していますが、有効期限を 20 秒に設定していますので、20 秒以内に同じリクエストを再送すると下図のようになります。

サーキット状態が OPEN であれば、サービスを呼び出さず、すぐに Fail ステートでステートマシンを終了させていますね。

もし、20秒以上経過してからリクエストを再送した場合は、DynamoDB テーブルの項目は有効期限が経過しているので削除されているか、もしくは項目が残っていても Query で ExpireTimeStamp >現在エポック秒 の条件を指定してサーキット状態を取得しているので、項目は取得されずサーキット状態は OPEN ではないものと判断され、もう一度 Execute Lambda ステートを実行します。

その他のサーキットブレーカーパターンの実装例

サーキットブレーカーパターンの実装は他にもあります。例えば下記のブログ記事では、AWS Step Functions ではなく、AWS Lambda extensions を使用しています。

aws.amazon.com

上記のブログ記事の実装例は、サービス呼び出しが同期呼び出しを前提としています。またサーキットブレーカーのロジックを呼び出し元のサービス内に埋め込む形となります。 呼び出すサービスの状態は Amazon DynamoDB のテーブルへ保存していますが、Extensions 側でキャッシュでも維持しているところが興味深いですね。 また、呼び出すサービスの状態を定期的にチェックするために、Amazon EventBridge やチェック用の Lambda 関数を別途用意している点も、AWS Step Functions を使用するパターンとは異なります。 これはこれで、一つの実装パターンですね。

最後に

この記事では AWS Step Functions をサーキットブレーカーパターンの実装に用いるトピックについて扱いましたが、AWS Step Functions は他にも様々な適用ができます。 例えば、AWS 規範ガイドにある Sage パターン (厳密にいうと Sage におけるオーケストレーションのパターン)でも AWS Step Functions を活用できます。

docs.aws.amazon.com

このパターンでも、AWS Step Functions を活用することで実装の容易性や可視性、トレース性を向上できているといえます。

このように、AWS Step Functions は様々なパターンの実装に適用できる「使いやすい」サービスだなと改めて感じました。

AWS Step Functions を活用した実装パターンについては、今後も探求し続けたいと思います!


Node.js の AWS Lambda 関数におけるモジュールの扱いについて

2024 年 6 月15 日に、ふと AWS Lambda で Node.js の Lambda 関数について深掘りしてみたいなと思い、いくつかのドキュメントを参照してみると AWS Lambda のラインタイム Node.js 16 の Deprecation date が 2024 年 6 月 12 日 であると AWS Lambda の開発者ガイドに記載されていることに気づきました。

docs.aws.amazon.com

これも何かの縁、虫の知らせかと思い、AWS Lambda の Node.js について記事を書くことにしました。

今回は、モジュールの取り扱い方についてまとめていきます。

AWS Lambda では、Node.js 14 以降のラインタイムから ECMAScript (ES) モジュール を使用できます。

ただし、AWS マネジメントコンソールでランタイムが Node.js 16 の Lambda 関数を作成した場合、生成されるコードやそのファイルは CommonJS モジュールとして扱われます。

ES モジュールとして扱うか、 CommonJS モジュールとして扱うかでは、Lambda 関数のコードの書き方も異なるため注意が必要です。

docs.aws.amazon.com

aws.amazon.com

この辺の理解を自分なりに整理したいと思ったので、簡単な検証をしてみました。

AWS マネジメントコンソールで、ランタイムが Node.js 16 と 18 の Lambda 関数を作成し、それぞれ、どうすれば ES モジュールとして扱われるか、または CommonJS モジュールとして扱われるかを確認していきます。

準備:AWS マネジメントコンソールから Node.js の Lambda 関数を作成

ランタイムに Node.js 16 を指定して作成した場合

  • 下記のコードを含んだ index.js が生成されます。

このデフォルトのコードを、この記事では便宜上、CommonJS モジュールのコード と呼びます。

これは、もちろん問題なく実行できます。

exports.handler = async (event) => {
  // TODO implement
  const response = {
    statusCode: 200,
    body: JSON.stringify('Hello from Lambda!'),
  };
  return response;
};

ランタイムに Node.js 18 または 20 を指定して作成した場合

  • 下記のコードを含んだ index.mjs が生成されます。

このデフォルトのコードを、この記事では便宜上、ES モジュールのコード と呼びます。

これも、問題なく実行できます。

export const handler = async (event) => {
  // TODO implement
  const response = {
    statusCode: 200,
    body: JSON.stringify('Hello from Lambda!'),
  };
  return response;
};

では、これらのコードを使って検証していきます。


検証 1. ランタイムが Node.js 18 の Lambda 関数 の index.mjs に、CommonJS モジュールのコード で実行してみる

package.json ファイルは無しの状態です。

  • 結果、エラーになります。

これは、AWS Lambda では拡張子が .mjs のファイルを ES モジュールとして扱うためです。

{
  "errorType": "ReferenceError",
  "errorMessage": "exports is not defined in ES module scope",
  "trace": [
    "ReferenceError: exports is not defined in ES module scope",
    "    at file:///var/task/index.mjs:1:1",
    "    at ModuleJob.run (node:internal/modules/esm/module_job:195:25)",
    "    at async ModuleLoader.import (node:internal/modules/esm/loader:337:24)",
    "    at async _tryAwaitImport (file:///var/runtime/index.mjs:1008:16)",
    "    at async _tryRequire (file:///var/runtime/index.mjs:1057:86)",
    "    at async _loadUserApp (file:///var/runtime/index.mjs:1081:16)",
    "    at async UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1119:21)",
    "    at async start (file:///var/runtime/index.mjs:1282:23)",
    "    at async file:///var/runtime/index.mjs:1288:1"
  ]
}

検証 2. ランタイムが Node.js 18 でファイル名を index.js にして、CommonJS モジュールのコードで実行してみる

package.json ファイルは無しの状態です。

  • 結果、正常に実行されます。

これは、AWS Lambda では デフォルトでは拡張子が .js のファイルを CommonJS モジュールとして扱うためです。


検証 3. ランタイムが Node.js 18 でファイル名を index.cjs にして、CommonJS モジュールのコードで実行してみる

package.json ファイルは無しの状態です。

  • 結果、正常に実行されます。

これは、AWS Lambda では拡張子が .cjs のファイルを CommonJS モジュールとして扱うためです。


検証 4. ランタイムが Node.js 16 の Lambda 関数 の index.js に、ES モジュールのコードで実行してみる

package.json ファイルは無しの状態です。

  • 結果、エラーになります。

これは、AWS Lambda では デフォルトでは拡張子が .js のファイルを CommonJS モジュールとして扱うためです。

{
  "errorType": "Runtime.UserCodeSyntaxError",
  "errorMessage": "SyntaxError: Unexpected token 'export'",
  "trace": [
    "Runtime.UserCodeSyntaxError: SyntaxError: Unexpected token 'export'",
    "    at _loadUserApp (file:///var/runtime/index.mjs:1084:17)",
    "    at async UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1119:21)",
    "    at async start (file:///var/runtime/index.mjs:1282:23)",
    "    at async file:///var/runtime/index.mjs:1288:1"
  ]
}

ただし、package.json ファイルで type を module として指定することで、.js ファイルを ES モジュールとして扱うこともできます。

次の検証 5. で確認してみましょう。


検証 5. ランタイムが Node.js 16 の index.js に、ES モジュールのコードを記述し、package.json ファイルで type を module に指定して実行する

package.json の内容

{
  "name": "example",
  "type": "module",
  "description": "This package will be treated as an ES module.",
  "version": "1.0",
  "main": "index.js"
}
  • 結果、正常に実行されます。

拡張子が .js の場合は、package.json の type の指定で制御できることがわかりました。

では、拡張子が .cjs の場合はどうでしょう?次の検証 6. で確認してみます。


検証 6. ランタイムが Node.js 16 でファイル名を index.cjs にして、ES モジュールのコードを記述し、package.json ファイルで type に module を指定して実行する

package.json の内容

{
  "name": "example",
  "type": "module",
  "description": "test6",
  "version": "1.0",
  "main": "index.cjs"
}
  • 結果、エラーになります。

これは、AWS Lambda では拡張子が .cjs のファイルを CommonJS モジュールとして扱うためです。.cjs の場合は、package.json で type に module を指定しても、ES モジュールとは扱われません。

{
  "errorType": "Runtime.UserCodeSyntaxError",
  "errorMessage": "SyntaxError: Unexpected token 'export'",
  "trace": [
    "Runtime.UserCodeSyntaxError: SyntaxError: Unexpected token 'export'",
    "    at _loadUserApp (file:///var/runtime/index.mjs:1084:17)",
    "    at async UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1119:21)",
    "    at async start (file:///var/runtime/index.mjs:1282:23)",
    "    at async file:///var/runtime/index.mjs:1288:1"
  ]
}

検証 7. ランタイムが Node.js 16 でファイル名を index.mjs にして、ES モジュールのコードで実行してみる

package.json ファイルは無しの状態です。

  • 結果、正常に実行されます。

これは、AWS Lambda では拡張子が .mjs のファイルを ES モジュールとして扱うためです。

package.json ファイルの type には、module だけでなく commonjs という値を指定できます。

では、次の検証 8. のパターンで確認してみましょう。


検証 8. ランタイムが Node.js 18 の Lambda 関数 の index.mjs に、CommonJS モジュールのコードを記述し、package.json ファイルで type に commonjs を指定して実行する

package.json の内容

{
  "name": "example",
  "type": "commonjs",
  "description": "test.",
  "version": "1.0",
  "main": "index.mjs"
}
  • 結果、エラーになります。

これは、AWS Lambda では拡張子が .mjs のファイルを ES モジュールとして扱うためです。.mjs の場合は、package.json で type に commonjs を指定しても、CommonJS モジュールとは扱われません。

{
  "errorType": "ReferenceError",
  "errorMessage": "exports is not defined in ES module scope",
  "trace": [
    "ReferenceError: exports is not defined in ES module scope",
    "    at file:///var/task/index.mjs:1:1",
    "    at ModuleJob.run (node:internal/modules/esm/module_job:195:25)",
    "    at async ModuleLoader.import (node:internal/modules/esm/loader:337:24)",
    "    at async _tryAwaitImport (file:///var/runtime/index.mjs:1008:16)",
    "    at async _tryRequire (file:///var/runtime/index.mjs:1057:86)",
    "    at async _loadUserApp (file:///var/runtime/index.mjs:1081:16)",
    "    at async UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1119:21)",
    "    at async start (file:///var/runtime/index.mjs:1282:23)",
    "    at async file:///var/runtime/index.mjs:1288:1"
  ]
}

つまり、package.json ファイルの type の指定で制御できるのは、拡張子が .js のときだけということがわかりました。

package.json ファイルが無い場合、つまりデフォルトだと CommonJS モジュールとして扱われるので、拡張子が .js の場合で、ES モジュールとして扱いたい場合のみ package.json ファイルを作成し、type に module を指定すればよい、ということになります。


ここまでの整理

検証パターン数が多くなったので、ここでひとまず整理します。

まず、AWS マネジメントコンソールから Lambda 関数を作成した場合は下表のようになり、ランタイムにより異なります。

ランタイム Node.js 16 Node.js 18 以降
生成されるファイル index.js index.mjs
生成されるコード CommonJS モジュールを想定したコード ES モジュールを想定したコード

ただ、コードを CommonJS モジュールとして扱うか、ES モジュールとして扱うかは下表のようになります。

これは、Node.js 16 でも Node.js 18 以降でも変わりません。(厳密には Node.js 14 から現時点の最新の Node.js 20 までは同じです。)

ファイル拡張子 モジュールの扱い方
.mjs ES モジュールとして扱う
.cjs CommonJS モジュールとして扱う
.js デフォルトでは CommonJS モジュールとして扱う。ただし package.json で type に module を指定すれば ES モジュールとして扱う

最後に

今回の記事のポイントは、下記になります。

  • Lambda 関数のコードのファイルの拡張子により、モジュールの扱いが変わってくる
    • 拡張子が .js の場合は、package.json の type の指定で制御可能
  • AWS マネジメントコンソール で Lambda 関数を作成する場合、ランタイムが Node.js 16 の場合と、18 移行の場合で生成されるファイルの拡張子が異なる

これから新規のコードを書く場合は特に問題ないかもしれませんが、古くから公開されている各種サンプルのコードを使う場合は、CommonJS モジュールか、ES モジュールか、ファイルの拡張子は何か、package.json での type の指定は問題ないかを確認したほうが良さそうですね!


AWS Step Functions から AWS Lambda 関数を呼び出す方法を整理してみた

前回記事に引き続き、AWS Step Functions がテーマです。

(2024 年 6 月に検証した内容に基づいています。)

AWS Step Functions のステートマシンは、様々な AWS サービスと連携できます。

例えば、AWS Lambda 関数を呼び出したり、Amazon SNS のトピックにメッセージを発行したり、Amazon DynamoDB のテーブルに項目を Put することも可能です。

この連携のことを 「統合 (integration)」と呼ぶこともあります。

この統合には、最適化された統合AWS SDK サービス統合 の 2 種類があるとドキュメントにも記載されており、それぞれの違いやサポートしているサービスなどが記載されています。

docs.aws.amazon.com

ただし、ステートマシンから AWS Lambda 関数を呼び出す方法を考えた場合、最適化された統合AWS SDK サービス統合 だけでなく、もう一つの方法があります。

この方法については AWS Step Functions の開発者ガイド で詳細に説明されているわけではありませんが、Amazon ステートメント言語の仕様や古い AWS Step Functions の公開情報には例として掲載されています。

この方法は特に名前がないのですが、この記事では便宜上、"レガシー" と呼ぶことにします。

つまり、ステートマシンから AWS Lambda 関数を呼び出す場合は、最適化された統合AWS SDK サービス統合レガシー の 3 種類の方法があるということになります。

具体的な例を見ていきましょう。

次のステートマシンの Amazon ステートメント言語 (ASL) の例では、この3種類の方法を試しています。

{
  "StartAt": "Lambda invoke Legacy",
  "States": {
    "Lambda invoke Legacy": {
      "Next": "Lambda invoke Optimized",
      "Type": "Task",
      "Comment": "Get Circuit Status",
      "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:Hello:$LATEST",
      "Parameters": {
        "greeting": "Legacy"
      }
    },
    "Lambda invoke Optimized": {
      "Type": "Task",
      "Next": "Lambda Invoke SDK",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "arn:aws:lambda:ap-northeast-1:123456789012:function:Hello:$LATEST",
        "Payload": {
          "greeting": "Optimized"
        }
      },
      "OutputPath": "$.Payload"
    },
    "Lambda Invoke SDK": {
      "Type": "Task",
      "Resource": "arn:aws:states:::aws-sdk:lambda:invoke",
      "Parameters": {
        "FunctionName": "arn:aws:lambda:ap-northeast-1:123456789012:function:Hello",
        "Payload": {
          "greeting": "SDK"
        }
      },
      "Retry": [
        {
          "ErrorEquals": [
            "Lambda.ServiceException",
            "Lambda.AWSLambdaException",
            "Lambda.SdkClientException",
            "Lambda.TooManyRequestsException"
          ],
          "IntervalSeconds": 1,
          "MaxAttempts": 3,
          "BackoffRate": 2
        }
      ],
      "End": true,
      "OutputPath": "$.Payload"
    }
  }
}

このステートマシンでは、最適化された統合AWS SDK サービス統合レガシー の異なる方法で同じ AWS Lambda 関数を呼び出していますが、 ASL を見ると、定義内容が異なっていることがわかります。

例えば Resource の指定は方法毎に異なります。

方法 Resource の内容
レガシー arn:aws:lambda:ap-northeast-1:123456789012:function:Hello:$LATEST
最適化された統合 arn:aws:states:::lambda:invoke
AWS SDK サービス統合 arn:aws:states:::aws-sdk:lambda:invoke

また、Parameters の指定は、レガシー だけが異なります。

  • レガシーの場合
"Parameters": {
        "greeting": "Legacy"
      }
  • レガシー以外の場合
"Parameters": {
        "FunctionName": "arn:aws:lambda:ap-northeast-1:123456789012:function:Hello:$LATEST",
        "Payload": {
          "greeting": ...
        }
      }

さらに、このステートマシンを実行したときに Lambda関数がリターンした値は、AWS SDK サービス統合 だけが異なります。 これについては、ドキュメントにも記載があり、AWS SDK サービス統合 の場合、レスポンスの Payload フィールドは、JSONエスケープした文字列 から JSON への解析・変換が行われません。 docs.aws.amazon.com

  • AWS SDK サービス統合の場合

  • AWS SDK サービス統合以外の場合


では、AWS マネジメントコンソールでステートマシンを作成する場合、これら 3 つの方法を指定できるでしょうか?

AWS マネジメントコンソールから GUI でステートマシンを作成する場合、[アクション] のコンポーネントで ”AWS Lambda invoke” をドラッグ & ドロップしてステートマシンを作成する限りは、最適化された統合 または AWS SDK サービス統合 になるので、レガシー になることはありません。

ただ、コードのエディタで レガシー を使った ASL を記述することは可能です。それを AWS マネジメントコンソール で GUI デザインのモードで表示すると、一見 最適化された統合AWS SDK サービス統合 と同じように表示されます。

しかしながら、右側に表示されるプロパティの設定エリアでは、最適化された統合AWS SDK サービス統合レガシー でそれぞれ異なります。

下図は レガシー のプロパティを表示した時のものです。ベーシック というリンクがあり、それをクリックすると吹き出しで補足説明が表示されます。

また上図の吹き出しの説明文を読む限り、最適化された統合 または AWS SDK サービス統合 が推奨のように読み取れますね。

ただし、世の中で公開されているステートマシンのサンプルには レガシー が使われているものもありますので、このような方法がある、ということは留意しておいた方が良さそうです。


このように、ステートマシンから AWS Lambda 関数を呼び出す方法については、これまでは何となくは理解していたのですが詳細までは把握できていませんでした。

しかし、今回改めて確認し、記事にまとめることで自分の中で整理できたのでよかったと思ってます。

どなたかの参考になれば嬉しいです!


Amazon API Gateway から AWS Step Functions のステートマシンを同期的に呼び出してみる

半年ぶりの記事となります。 たまたま、AWS Step Functions についていくつか調査する機会があったので久々に記事にまとめようと思い立ちました。

タイトルの処理を実装する手順をまとめていきますが、Amazon API Gateway から AWS Step Functions のステートマシンを非同期で呼び出す手順は AWS の公式ドキュメントに手順が記載されています。

docs.aws.amazon.com

この処理は、下図のような仕組みで実現できます。

ただ、同期的に呼び出す手順については明確には記載されていないので、この記事にまとめていきます。


Agenda


今回実装する処理

今回は、Amazon API GatewayAPI にクエリパラメータ yourname で指定した名前に、Hello というメッセージをつけて同期的に返す処理を実装していきます。


ステートマシンの実装

AWS Step Functions のステートマシンは、この処理を実現する最低限の実装にしています。具体的には、Express タイプ のステートマシンで、Pass ステートだけを使用し、レスポンスとなるメッセージにステートマシンのパラメータの値を設定して出力します。

{
  "Comment": "A description of my state machine",
  "StartAt": "Pass",
  "States": {
    "Pass": {
      "Type": "Pass",
      "Parameters": {
        "message.$": "States.Format('Hello, {} !', $.yourname)"
      },
      "End": true
    }
  }
}

Express タイプのステートマシンは、同期または非同期で呼び出せます。Express タイプと 標準タイプの違いは下記のドキュメントにまとまっています。

docs.aws.amazon.com


Amazon API Gateway の設定

リクエストの処理について下図のように設定していきます。

まず、下記を参考に API から ステートマシンを実行する許可を与えるための IAM ロールを作成しておきます。

docs.aws.amazon.com

次に REST API を新規に作成します。リソースの設定は任意ですが、メソッドは GET を作成します。

統合リクエストでは、下表のように設定します。同期リクエストの場合は アクション名に StartSyncExecution を指定するのがポイントです。

設定項目 設定内容
統合タイプ AWS のサービス
AWS リージョン (使用しているリージョン)
AWS のサービス Step Functions
HTTP メソッド POST
アクション名 StartSyncExecution
実行ロール (作成した実行ロールの ARN)

そして、統合リクエストのマッピングテンプレートで下記を設定します。これは、yourname というクエリパラメータ の値をステートマシンに渡す JSON に変換しています。stateMachineArn には作成したステートマシンの ARN を設定します。

コンテンツタイプ は、application/json

{
    "input": "{ \"yourname\": \"$input.params('yourname')\"}",
    "name": "MySyncExecution",
    "stateMachineArn": "arn:aws:states:ap-northeast-1:123456789012:stateMachine:sfnsync-statemachine"
}

次に、下図のように統合レスポンスマッピングテンプレートを設定します。この設定により、ステートマシンが出力した内容だけを API の呼び出しもとに返すようにできます。

コンテンツタイプ は、application/json

$input.path('$.output')

以上で API の設定は完了です。


テスト実行

では、AWS マネジメントコンソールのテストの機能を使って、API から ステートマシンを呼び出してみましょう [テスト] タブをクリックして、[クエリ文字列] に yourname=John Doe と入力し、[テスト] をクリックします。

レスポンスとして下図のように表示されれば OK です!

作成した API をデプロイする場合は、[API をデプロイ] をクリックし、ステージを指定してデプロイします。


最後に

Express タイプのステートマシンだと短時間の処理を同期的に呼び出すケースもありえますが、Amazon API Gateway を使った場合の同期的な呼び出しも問題なく行えることがわかりました。 ただ、マッピングテンプレートを活用する必要があるので、下記のドキュメントなどで理解を深めておくことも重要ですね。

docs.aws.amazon.com


/* -----codeの行番号----- */