FastAPI をLambda + API Gatewayで公開してSwagger UIで確認する

# 本記事の目的

Lambda + API Gatewayで構築した REST API を公開し、Swagger UI によって視覚的にAPIの確認ができるようになることをゴールとします。
なお、フレームワークにはFast APIを使用し、ECRを経由してDockerイメージをLambdaに展開することを想定しています。

AWSの構成は以下の通りです。

![fastapi-lambda.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821799/718b2f59-44fd-436e-a316-a18f6e203abc.png)


# 背景

現時点で対応する画面が無いREST APIの開発を進めていく上で、クライアントへのAPIの見せ方について検討したところ、Swagger UIを利用できれば、工数をかけずに視覚的な操作を提供できるという結論から、本記事の作成に至りました。
その中で、Lambdaに乗せることを考慮した設定がいくつか必要だったこともあり、誰かのお役に立てれば幸いです。

# 前提

本記事は、Swagger UIとかLambdaとか名前はわかるけど、あんまり使ったことないしとりあえず構築の仕方だけ知りたい！といったお急ぎの方を想定して作成しています。

そのため、本記事ではAPIのローカル開発環境や詳細な設定値については触れず、APIを公開し、Swagger UIを確認するための手順が主として説明しています。


個々の詳細は追って記事を作成したいと思うだけ思ってます。

# 手順

1. Fast APIでREST APIの作成
1. ECRにイメージをプッシュ
1. Lambdaでイメージの展開
1. API Gatewayのセットアップ

## Fast APIでREST APIの作成

Fast APIの立ち上げ手順については割愛し、以下のリポジトリを使用します。

https://github.com/RHIC-7/swagger-sample

Lambdaに乗せる場合には、以下の点に注意する必要があります。

1. openapi_prefixにAPI Gatewayのステージ名を入力する
1. Mangumを使用してlambda_handlerを定義する

```python
app = FastAPI(openapi_prefix='/sample/')  # ← API Gatewayのステージ名
app.include_router(sample.router)

lambda_handler = Mangum(app) # ← lambda_handlerをMangumを使用して定義する
```


### ポイント① 「openapi_prefixにAPI Gatewayのステージ名を入力する」

API Gatewayを経由してアプリケーションを公開する場合、デフォルトのアプリケーションルートへのURLは、
`https://{API Gatewayのドメイン名}/{ステージ名}/`
とアプリケーションルートとドメインの間にステージ名が入る構造になっているため、Swagger UIがリクエストするOpenAPIの定義ファイルへのパスとの不整合が発生し、403エラーが発生します。

:::note info

API Gatewayでは、定義されていないリソースパスへのリクエストを行った場合、「リソースパスが存在しない」という403 HTTPレスポンスが返されます。
```
"x-amzn-errortype" = "MissingAuthenticationTokenException"
```

参考
https://repost.aws/ja/knowledge-center/api-gateway-troubleshoot-403-forbidden

:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821799/59f56c5b-d698-450a-985b-e418b4612adc.png)

これを回避するために、Fast APIクラスの引数で
`openapi_prefix='/{ステージ名/'`（デフォルト値は `''`）
を指定してあげることで、正常に表示することができるようになります。


### ポイント② 「Mangumを使用してlambda_handlerを定義する」

Lambdaはlambda_handlerという関数がメインメソッドとして機能します。そのため、Fast APIなどで定義したapp.pyなどのメインファイルだけでは直接的に動かすことができません。
そこで、WSGIアプリケーションやASGIアプリケーションを動かすためには専用のアダプターが必要となります。


今回は、ASGIアプリケーションアダプターのMangumを利用してlambda_handlerからアプリケーションへリクエストを流してもらうように設定します。

```python
from mangum import Mangum

lambda_handler = Mangum(app)
```

## ECRにイメージをプッシュ

Dockerfileを定義して、イメージをビルドします。

```bash
docker build -t swagger-sample .
```

管理しやすくするために、作成したイメージにタグ付けをします。

```bash
docker tag swagger-sample:latest {アカウントID}.dkr.ecr.ap-northeast-1.amazonaws.com/swagger-sample:latest
```

レジストリカタログへプッシュするために、ECRにログインします。
ECRへのイメージのプッシュを許可するポリシーがアタッチされているEC2上で作業をしている場合は、以下のコマンドでログインが可能です。

```bash
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin {アカウントID}.dkr.ecr.ap-northeast-1.amazonaws.com
```

最後にプッシュします。

```bash
ocker push {アカウントID}.dkr.ecr.ap-northeast-1.amazonaws.com/swagger-sample:latest
```

## Lambdaでイメージの展開

AWSマネジメントコンソールから、
前段でプッシュしたイメージをコンテナイメージURIとして選択し、Lambda関数を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821799/ed6195bb-82c7-43e5-8af3-d9914d5c28ae.png)

## API Gatewayのセットアップ

Lambda関数を作成したら、最後の仕上げに移ります。

1. 「API Gateway」サービス → 「APIの作成」→ 「REST API」と移動
1. API名を入力し他はデフォルト設定のまま、下部の「APIを作成」を押下
1. 作成したAPIの詳細画面で、「メソッドを作成」を押下し、以下を設定
    - **メソッドタイプ**：GET （必要に応じて追加）
    - **統合タイプ**：Lambda関数
    - **Lambda プロキシ統合**：ON
    - **Lambda関数**：前段で作成したLambda関数を選択
    - **その他**：デフォルト（必要に応じて設定）
1. 作成したAPIの詳細画面に戻り、「リソースの作成」を押下し以下を設定
    - **プロキシのリソース**：ON
    - **リソース名**：{proxy+}
1. 追加された `/{proxy+}` のANYメソッドを押下し、「統合の編集」を押下
    - **統合タイプ**：Lambda関数
    - **Lambda プロキシ統合**：ON
    - **Lambda 関数**：前段で作成したLambda関数を選択
    - **その他**：デフォルト
1. 作成したAPIの詳細画面に戻り、「APIをデプロイ」を押下
1. 新しいステージを作成し、「デプロイ」

:::note warn
新しいステージを作成する際に、`openapi_prefix` の値と一致していることを確認しましょう。
:::

## ブラウザからSwagger UIの確認

Fast APIのデフォルト設定では、 `/docs` がSwagger UI用のエンドポイントとして定義されています。
そのため、以下のURLで検索をかけることで、Swagger UIが閲覧できるようになっていると思います。

```
https://{デプロイ時に作成されたAPI Gatewayのエンドポイント（ステージ名を含む）}/docs
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821799/bed947c4-ef1d-4cdb-b0db-5458444989d6.png)


# まとめ

今回はFast APIを使用したアプリケーションを Lambda + API Gateway で公開し、Swagger UIでAPIを視覚的に確認するという手順の紹介をしました。

Fast APIでは、Swagger UIを標準的に組み込まれており、ちょっとした設定をするだけでSwagger UIでのAPI確認が可能となっています。

Lambda + API Gateway上で公開する場合のポイントとして、
- API Gatewayを介す場合、OpenAPI定義ファイルのデフォルトパスがずれてしまうため、プレフィックスを指定する必要がある
- Fast API上でASGIアプリケーション用アダプタを使用してlambda_handlerメソッドを定義する必要がある

の2点を挙げています。自分のようなAPIもインフラも初心者の方の参考になれば幸いです。

アダプタやCORSの細かい話はまたどこかで取り上げたいと思っています。
最後まで読んでいただきありがとうございました。

# 参考資料

本記事の作成にあたり、以下の記事を参考にさせていただきました。

https://fastapi.tiangolo.com/ja/reference/fastapi/

https://qiita.com/Shinkijigyo_no_Hitsuji/items/cedd1825e5437663d3ce

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-rest-api.html