メインコンテンツに移動
デベロッパーのためのクラウド活用方法

AWS Lambda Powertools Python入門 第 5 回 EventHandler Utility - REST API 編

2022-09-02 | Author : 福井 厚

はじめに

皆さん、こんにちは。AWSソリューションアーキテクトの福井です。本連載は、AWS Lambda Powertools Pythonの提供する様々なユーティリティーについて、その利用方法を紹介するシリーズですが、この度、Lambda Powertools for TypeScript も一般利用可能 (GA) となりました。普段、Lambda 関数を TypeScript で記述されている方々にとっては嬉しいお知らせではないでしょうか。この連載を参考にしつつ、TypeScript 版もぜひお試しください。

さて、第 4 回 では、AWS Lambda Powertools Python のMetricsユーティリティーの使い方についてご紹介しました。今回は Core Utilities の中の Event Handler についてご紹介します。なお、本シリーズでは、AWS Lambda Powertools Python 1.25.1 を対象としています。


X ポスト » | Facebook シェア » | はてブ »

builders.flash メールメンバー登録

builders.flash メールメンバー登録で、毎月の最新アップデート情報とともに、AWS を無料でお試しいただけるクレジットコードを受け取ることができます。 
今すぐ登録 »

Event Handler (Core Utilities)

Lambda Powertools Python の Event Handler は、REST API と GraphQL API の両方が提供されています。今回は REST API について紹介し、GraphQL API は次回紹介する予定です。

REST API

Amazon API Gateway の REST API、HTTP API、および Application Load Balancer (ALB) のためのイベントハンドラを提供します。REST API イベントハンドラの主要な機能は以下の通りです。

  • API Gateway REST / HTTP API と ALB に対するボイラープレートを減らす軽量なルーティング
  • CORS、バイナリーと Gzip 圧縮、Decimal の JSON エンコーディング、独自の JSON シリアライザをサポート
  • 自己記述型イベントスキーマのための Event Source Data Classes ユーティリティーとの統合

利用方法

AWS Lambda Powertools Python を Lambda 関数に組み込む方法については、第 1 回 の記事を参照ください。既存の Amazon API Gateway でプロキシ統合を設定するか、ALB で Lambda 関数を呼び出すように設定しておく必要があります。

AWS Serverless Application Model (SAM) の例です。

AWS Serverless Application Model (SAM) の例

コマンド / コード

python
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  restapi-eventhandler-demo

Globals:
  Api:
    TracingEnabled: true
    Cors:
      AllowOrigin: "'*'"
      AllowHeaders: "'Content-Type,Authorization,X-Amz-Date'"
      MaxAge: "'300'"
    BinaryMediaTypes:
      - "*~1*"
    EndpointConfiguration:
      Type: REGIONAL
  Function:
    Timeout: 5
    Runtime: python3.9
    Tracing: Active
    Environment:
      Variables:
        LOG_LEVEL: INFO
        POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1
        POWERTOOLS_LOGGER_LOG_EVENT: true
        POWERTOOLS_SERVICE_NAME: restapi-eventhandler-demo

Resources:
  RestAPiFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./src
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Layers:
        - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPython:11
      Events:
        AnyApiEvent:
          Type: Api
          Properties:
            Path: /{proxy+}
            Method: ANY

イベントリゾルバ

関数のデコレーターをセットしてリクエストの path や HTTP のメソッドをハンドルする前に、リゾルバを初期化する必要があります。リゾルバはリクエストを解決するために、1 つ以上のルーティング処理を含みます。また型付けされたプロパティーを通じて現在のイベントにアクセスできるようにします。

リゾルバとして、APIGatewayRestResolver、APIGatewayHttpResolver、ALBResolver を提供しています。

Amazon API Gateway REST API

Lambda 関数のフロントとして、Amazon API Gateway の REST API を利用する場合は、APIGatewayRestResolver を利用することができます。

例えば、Amazon API Gateway の REST API から GET リクエストの path で /products をハンドルする方法は以下の通りです。

Screenshot of Python code demonstrating the use of AWS Lambda Powertools, featuring an API Gateway Rest Resolver to handle GET requests to '/products' and return a list of products. Key parts of the code are highlighted, showing imports, tracer, logger, and handler setup.

レスポンスの例

API Gateway の公開している URL に /products のパスをつけて呼び出すと Lambda 関数は API Gateway に対して以下のレスポンスを返します。

json
{ "statusCode": 200, "headers": { "Content-Type": "application/json" }, "body": "{\"products\":[{\"id\":\"0001\",\"name\":\"M01\",\"price\":1000}, {\"id\":\"0002\",\"name\":\"M02\",\"price\":2000}, {\"id\":\"0003\",\"name\":\"M03\",\"price\":1500}]}", "isBase64Encoded": false }

クライアントへのレスポンス

このレスポンスを受け取った Amazon API Gateway は、Response の StatusCode として 200 を返し、以下の JSON メッセージをクライアント側に返します。 このように、ルートに対するメソッドを定義し、辞書型のオブジェクトをリターンするだけのシンプルな実装で、クライアント側にその値を JSON として返すことができます。

json
{
    "products": [
        {
            "id": "0001",
            "name": "M01",
            "price": 1000
        },
        {
            "id": "0002",
            "name": "M02",
            "price": 2000
        },
        {
            "id": "0003",
            "name": "M03",
            "price": 1500
        }
    ]
}

Amazon API Gateway HTTP API

Amazon API Gateway の HTTP API を利用する場合は以下のようなコードになります。違いは、APIGatewayRestResolver の代わりに APIGatewayHttpResolver を利用するところです。

Screenshot of a Python code example using AWS Lambda Powertools to implement an API Gateway HTTP resolver with logger and tracer utilities. The code demonstrates how to set up an endpoint to retrieve product information using Lambda functions.

Application Load Balancer

同様に、ALB のロードバランサをイベントソースとして Lambda 関数を実行する場合は、APIGatewayRestResolver の代わりに、ALBResolver を利用します。

動的ルーティング

動的な URL パスを構成するために、/products/<product_id> の形式を利用することができます。<product_id> は、実行時に解決されます。

個々の動的ルーティングは関数のシグネチャの一部である必要があります。これによって動的ルーティングにマッチした時に、キーワード引数を利用して関数が呼び出されます。

Screenshot of a Python code snippet using AWS Lambda Powertools. The code defines a REST API endpoint that retrieves product details by product ID, iterating through a list of products and returning a matching product or a 'no match' message.

0001 での実行結果

この Lambda 関数をデプロイして、API Gateway の URL に /products/0001 のパスをつけて呼び出した結果は以下の通りです。

json
{"id":"0001","name":"M01","price":1000}

0004 での実行結果

同様に /products/0004 のパスで実行すると以下のような結果がクライアント側に返ります。動的パスはネストすることも可能です。例えば、/products/<product_id>/<product_status> のような呼び出しもできます。

json
{"message":"no match!"}

すべてのルートを補足する

正規表現を利用してリクエストに含まれる任意の数のパスを取り扱うことができます。例えば、“ .+” を指定するとすべてのパスを補足することが可能です。ただし、すべてのルートを補足することは、できるだけ控えて可能な限り明示的にルートを指定されることをお勧めします。
Screenshot of a Python code snippet using the @app.get('.+') decorator to catch any GET route, with a function named catch_any_route_get_method. Related to AWS Lambda Powertools for Python.

HTTP メソッド

Lambda 関数でハンドルする HTTP メソッドを指定するために名前付きデコレーター (app.get など) を利用することができます。指定可能なメソッドは、get, post, put, patch, delete, option です。

例えば、post メソッドをハンドルするには、このようなコードを記述します。

Screenshot showing a Python code snippet that defines an API endpoint using @app.post('/products') in AWS Lambda Powertools. The function create_product captures method execution and processes JSON body input, with a comment about saving product data to a data store.

複数の HTTP メソッド

もし 1 つの関数で複数の HTTP メソッドを受けたい場合は、route メソッドを利用して HTTP メソッドのリストを送ります。

一般的には、HTTP メソッドごとに個別の関数を定義するほうが望ましいです。なぜなら関数で実装する機能は、どの HTTP メソッドを利用するかに依存するからです。

Screenshot of a Python code snippet using AWS Lambda Powertools. The code shows a route decorator for '/products' using PUT and POST methods, a tracer decorator, and a function to create product data from the current event's JSON body. The comment indicates creating product data and saving it to a data store.

リクエストの詳細へのアクセス

Event Handler は、Event Source Data Classes Utility と統合されており、このユーティリティーは個々のリゾルバのリクエストの詳細と app.current_event の便利なメソッドを提供しています。

すべてのサンプルソースに app.resolve(event, context) のコードが記述されています。これによって Event Handler がリクエストを解決することができ、app.lambda_contextapp.current_event のようなデータを取得することができます。

クエリ文字列とペイロード

app.current_event プロパティ内の query_string_parameters を通じて、すべての利用可能なクエリ文字列にアクセスすることができます。または特定のクエリ文字列を get_query_string_value メソッドで取得することができます。

body プロパティを利用すると生のペイロードにアクセスすることができます。また、Body の 値が JSON 文字列の場合は、 上記の HTTP メソッドの app.post のサンプルコードのように、json_body プロパティを利用して素早くデシリアイズすることができます。

Screenshot of a Python code snippet demonstrating usage of AWS Lambda Powertools to handle product queries by query string, showing a function 'get_product_by_qs' which retrieves products based on a product ID from the query string.

headers の取得

クエリ文字列と同様に、 app.current_event.headers を通じて headers の値をディクショナリとして取得できます。また get_header_value を利用して特定の名前を指定して値を取得することができます。
Screenshot of Python code demonstrating AWS Lambda Powertools Tracer usage. The code shows a FastAPI endpoint decorated with tracer.capture_method, extracting a trace_id from the request header 'X-Amzn-Trace-Id' and returning it in a response JSON object.

実行結果

実行結果は以下の通りです。

json
{"trace_id":"Root=1-62e8614b-5dc882a310b97e464ab07ee4"}

not found ルートのハンドリング

デフォルトでは、すべてのルートにマッチしない場合は 404 ( {"statusCode":404,"message":"Not found"}) を返します。not_found デコレーターを使用してこの振る舞いを上書きして、カスタムのレスポンスを返すことができます。
Screenshot of Python code using AWS Lambda Powertools to handle 'not found' errors with custom logging and JSON response in an API Gateway REST handler, including Japanese error message in the response body.

レスポンスの例

このコードをデプロイして存在しないパスを指定して呼び出すと API Gateway に対して以下のレスポンスを返します。

json
{ "statusCode": 404, "headers": { "Content-Type": "application/json" }, "body": "{\"StatusCode\": 404, \"error_message\": \"\u898b\u3064\u304b\u308a\u307e\u305b\u3093\"}", "isBase64Encoded": false }

クライアントへのレスポンスの例

このレスポンスを受けて API Gateway はクライアントに StatusCode 400 と共に以下の結果が返されます。

json
{"StatusCode": 404, "error_message": "見つかりません"}

例外のハンドリング

あらゆる Python の例外に対して exception_handler デコレーターを使用することができます。例えば、バリデーションエラーのようなルート以外の一般的な例外をハンドルすることができます。
Screenshot of Python code demonstrating the use of AWS Lambda Powertools for Python, showing a custom exception handler for ValueError and an endpoint to get product quantity with error handling and tracing decorators.

実行結果の例

この Lambda 関数に対して、/products/1 のパスを指定した場合クライアントに {"quantity":1} のレスポンスが返りますが、数字以外の値をパラメータとしてパスに指定すると ValueError が発生します。例えば、/products/a を指定すると StatusCode 400 と共に以下のメッセージがクライアント側に返されます。

bash
Invalid request parameters.

HTTP エラーを発生させる

ServiceError 例外を使用することで、あらゆる HTTP エラーを発生させクライアントに戻すことができます。これによって Lambda 関数が失敗することなく、エラーを通知するための正しい HTTP レスポンスをクライアントに返すことができます。

Tips:もしカスタムヘッダを送信したい場合は、代わりに Response クラスを利用します。

HTTP 400、401、404、500 など、良く利用されるエラーは事前定義エラーとして提供されています。

Screenshot of a Python code example demonstrating exception handling with AWS Lambda Powertools, showing import statements for various exception classes and route handlers for different error types in an API Gateway REST resolver.

実行結果の例

この Lambda 関数に対して、例えば /unauthorized-error パスを指定して実行すると StatusCode 401 と共に以下のメッセージがクライアント側に返ります。 このように簡単に HTTP エラーを生成して返すことができます。

json
{"statusCode":401,"message":"Unauthorized"}

CORS

APIGatewayRestResolver のコンストラクタの CORSConfig クラスを利用した cors パラメータを通じて、CORS の設定を行うことができます。これによってパスに一致した関数が実行される時に、常にCORSヘッダーをレスポンスの一部として返すことを保証します。

Tips: cors=False とパラメータを指定することで、オプションで CORS を無効にすることができます。

Screenshot of a Python code example demonstrating the use of AWS Lambda Powertools for logging, tracing, and API Gateway REST resolver with CORSConfig. The highlighted section shows CORS configuration and an example function for handling product data via API.

レスポンス

この Lambda 関数に対して /products のパスで API Gateway を呼び出すと、次のレスポンスを API Gateway に返します。

json
{
    "statusCode": 200,
    "headers": {
        "Content-Type": "application/json",
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Headers": "Authorization,Content-Type,X-Amz-Date,X-Amz-Security-Token,X-Api-Key",
        "Access-Control-Max-Age": "300"
    },
    "body": "{\"products\":[{\"id\":\"0001\",\"name\":\"M01\",\"price\":1000},{\"id\":\"0002\",\"name\":\"M02\",\"price\":2000},{\"id\":\"0003\",\"name\":\"M03\",\"price\":1500}]}",
    "isBase64Encoded": false
}

クライアントへのレスポンス

API Gateway はこのレスポンスを以下のレスポインスヘッダへ変換してクライアントに返します。

json
{
  "Access-Control-Allow-Headers": [
    "Authorization,Content-Type,X-Amz-Date,X-Amz-Security-Token,X-Api-Key"
  ],
  "Access-Control-Allow-Origin": ["*"],
  "Access-Control-Max-Age": ["300"],
  "Content-Type": ["application/json"],
  "X-Amzn-Trace-Id": ["Root=1-62ef9c7b-9750387727c0f180c8db7007;Sampled=1"]
}

まとめ

いかがでしたでしょうか。今回は、AWS Lambda Powertools Python の EventHandler ユーティリティーの REST API について紹介しました。

Event Handler Utility を利用することで Amazon API Gateway や ALB からの Lambda 関数へのイベントのハンドルを容易にし、その実装も容易になることが理解できたかと思います。Amazon API Gateway の  Lambda プロキシ統合や ALB と Lambda 関数の統合を実装する際には、この機能を組み込むことをお勧めします。次回は EventHandler の GraphQL API について紹介します。次回もお楽しみに。

筆者プロフィール

福井 厚
アマゾン ウェブ サービス ジャパン合同会社
シニアソリューションアーキテクト
Developerスペシャリスト - DevAx

2015 年からアマゾンウェブサービスジャパンでソリューションアーキテクトとして活動。最近は、Developer スペシャリスト SA として日々開発者の方にクラウドネイティブなモダンアプリケーション開発の技術支援を行なっています。

A portrait photograph of a smiling man wearing glasses, a beard, and a red sweater over a pink shirt. The image is titled 'Portrait of Fukui Atsushi, 2021.'