aws-lambda-powertoolsを利用してLambdaでAPIを作る

## はじめに
私たちのチームでは、企業が利用している人事システムなどのデータを取り込み、集計して、人的資本にまつわる様々な指標を表示したり、業界平均などと比較することのできるWebアプリケーションを開発しています。

WebアプリケーションのAPI（サーバーサイド）を、API Gateway + Lambda（Python）で構成しています。
その際に、 [aws-lambda-powertools](https://docs.powertools.aws.dev/lambda/python/latest/) を使って、とても便利だと感じたので、紹介します。

なお、この記事で紹介する aws-lambda-powertools の機能は、バージョン2.15.0時点のものです。

## 便利な点の紹介

この記事では、実際にアプリケーションで利用しているLambdaのソースコードを用いて、 aws-lambda-powertools の便利な使い方を紹介していきます。

```python
from http import HTTPStatus

from _types.errors import ClientError
from aws_lambda_powertools import Logger
from aws_lambda_powertools.utilities.data_classes import APIGatewayProxyEvent, event_source
from aws_lambda_powertools.utilities.parser import parse
from aws_lambda_powertools.utilities.typing import LambdaContext
from handlers.middleware_after import middleware_after
from handlers.tracer import tracer

logger = Logger(service="hci", log_uncaught_exceptions=True)    # 便利な点① ロギング


@tracer.capture_lambda_handler  # 便利な点② トレース
@logger.inject_lambda_context()
@middleware_after   # 便利な点③ ミドルウェア
@event_source(data_class=APIGatewayProxyEvent)
def lambda_handler(event: APIGatewayProxyEvent, context: LambdaContext):
    tenant_id = event["requestContext"]["authorizer"]["tenant_id"]
    logger.info(f"tenant_id: {tenant_id}")

    request = parse(event=event["body"], model=CompanyCreateRequest)    # 便利な点④ リクエストのパース

    # ...省略...

    try:
        usecase.create(command)
        return {"statusCode": HTTPStatus.NO_CONTENT.value}
    except ClientError as e:
        logger.error(e)
        return {"statusCode": HTTPStatus.BAD_REQUEST.value, "body": e.message}

```
### 便利な点① トレース（[参照](https://docs.powertools.aws.dev/lambda/python/latest/core/tracer/)）

使い方は以下の2ステップです。

1. aws-lambda-powertoolsからインポートしたTracerクラスのインスタンス化
1. handler関数へのデコレータの追加

これだけで、APIリクエストのトレース情報がCloudWatchに送られます。
APIリクエストによって実行されるLambda AuthorizerやLambda、DynamoDBへのアクセスにかかる時間などが確認できます。
![スクリーンショット 2024-03-27 23.20.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614712/68e425c5-ddb6-c7e1-e073-55f60377c293.png)

Lambdaを利用してAPIを構築すると、API GatewayやLambda Authorizer、Lambdaなどさまざまなサービスに分散して処理が行われるため、エラー原因の特定が面倒だったり、レスポンス速度のボトルネックの発見が難しかったりします。
トレースを確認することで、これらの発見がいとも簡単に行えます！

### 便利な点② ロギング（[参考](https://docs.powertools.aws.dev/lambda/python/latest/core/logger/)）

使い方は以下の3ステップです。

1. aws-lambda-powertoolsからインポートしたLoggerクラスのインスタンス化
2. handler関数へのデコレータの追加
3. ロジック内で利用

ロジック内に自分で記述したログだけでなく、以下のようなLambda関数の情報もログ出力することができます。
Lambdaのコールドスタートの情報などもわかって良いですね！

```json
{
    "level": "INFO",
    "location": "lambda_handler:35",
    "message": "tenant_id: 19206e1e-9d9a-4301-b81c-xxxxxxxxxx",
    "timestamp": "2024-03-23 13:31:42,315+0000",
    "service": "hci",
    "cold_start": true,
    "function_name": "companies-create",
    "function_memory_size": "128",
    "function_arn": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:companies-create",
    "function_request_id": "69423838-9cb7-4376-9996-7c439df6d855",
    "xray_trace_id": "1-64d243bc-xxxxxxxxxx"
}
```

ログ内に記載された `xray_trace_id` を利用することで、CloudWatch の X-Ray トレースの画面から簡単に該当リクエストのトレースを検索できます。

### 便利な点③ ミドルウェア（[参考](https://docs.powertools.aws.dev/lambda/python/latest/utilities/middleware_factory/)）

使い方は以下の2ステップです。

1. ミドルウェア関数の定義（後述）
1. handler関数へのデコレータの追加

ミドルウェアを利用することで、handler関数の処理の前後に別の処理を実行することができます。
今回の例では、以下のようにミドルウェア関数を定義し、handler関数が返還するレスポンスのヘッダーにCORSポリシーを設定しています。

```python
from typing import Callable

from aws_lambda_powertools.middleware_factory import lambda_handler_decorator
from config.index import FRONT_ORIGIN


@lambda_handler_decorator
def middleware_after(handler, event, context) -> Callable:
    response = handler(event, context)
    response["headers"] = {"Access-Control-Allow-Origin": FRONT_ORIGIN}
    return response
```

LambdaでAPIを構築すると、とても多くのLambdaが作られると思いますが、お決まりの処理をミドルウェアで行うことで、実装コストが下がってとても良いです！

### 便利な点④ リクエストのパース（[参考](https://docs.powertools.aws.dev/lambda/python/latest/utilities/parser/)）

使い方はたった1つ、 aws-lambda-powertool からインポートした parse 関数にリクエストボディとPydanticのモデルクラスを渡すだけです。

これだけで、リクエストボディを検証することができ、また後続で型定義された情報を利用して処理することができます。
便利！!

## おわりに

実際の開発で利用した aws-lambda-powertools の機能をご紹介しました。
まだまだたくさんの機能があるので、どんどん使っていきたいと思います！