FastAPIとReactをAWSでサーバーレス構築!Lambda・S3・CloudFront徹底解説!

aws

 FastAPIとReactを使った構築方法において、世の中に出回っている情報はローカル環境での構築方法しか出回っておらず、クラウドサービスを使った構築方法が見当たらないなと感じました。これはAWS初心者にとって大きなハードルだと感じました。そこでクラウドサービスを使った構築方法について一連の流れをまとめました。

バックエンドの環境構築

Python 3.11をインストール

 最新版は3.13ですが、LambdaでFastAPIを利用する場合、FastAPIのバージョンを0.99.0にする必要があります。それに対応しているのが3.11になります。公式サイトから3.11をダウンロードします。

 Pythonのバージョンが3.11であることを確認します。

python --version

※バージョンをいろいろ使い分けたい場合はpyenvをインストールします。

【任意】pyenvのインストール

 こちらのサイトの「README.md」を参照し、手動でインストールを行いました。

「Pyenv-win.zip」を押し、説明通り以下の操作を行いました。

 以下では、ステップ4までを行った後から説明します。

 .pyenvフォルダ内にpyenv-winが移動されていることを確認します。

Get-ChildItem $env:USERPROFILE\.pyenv

 環境変数「PYENV」、「 PYENV_HOME」、「PYENV_ROOT」を追加します。

[System.Environment]::SetEnvironmentVariable('PYENV',$env:USERPROFILE + "\.pyenv\pyenv-win\","User")

[System.Environment]::SetEnvironmentVariable('PYENV_ROOT',$env:USERPROFILE + "\.pyenv\pyenv-win\","User")

[System.Environment]::SetEnvironmentVariable('PYENV_HOME',$env:USERPROFILE + "\.pyenv\pyenv-win\","User")

 以下も実行します。

[System.Environment]::SetEnvironmentVariable('path', $env:USERPROFILE + "\.pyenv\pyenv-win\bin;" + $env:USERPROFILE + "\.pyenv\pyenv-win\shims;" + [System.Environment]::GetEnvironmentVariable('path', "User"),"User")

 環境変数一覧をチェックします。

Get-ChildItem Env:

 Pathに設定されているパスをリストで確認します。

$env:Path -split ';'

[pyenv –version」を実行します。すると、以下のエラーが出てきました。実行ポリシーが許可されていないために発生しました。

pyenv : このシステムではスクリプトの実行が無効になっているため、ファイル C:\Users\USER\.pyenv\pyenv-win\bin\pyenv.ps
1 を読み込むことができません。詳細については、「about_Execution_Policies」(https://go.microsoft.com/fwlink/?LinkID=1351
70) を参照してください。
発生場所 行:1 文字:1
+ pyenv --version
+ ~~~~~
    + CategoryInfo          : セキュリティ エラー: (: ) []、PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess

そこで、現在の実行ポリシーを以下のコマンドで確認しました。

Get-ExecutionPolicy -List

結果

Scope ExecutionPolicy
        ----- ---------------
MachinePolicy       Undefined
   UserPolicy       Undefined
      Process       Undefined
  CurrentUser       Undefined
 LocalMachine       Undefined

現在のユーザーに権限を与えました。

Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned

結果

        Scope ExecutionPolicy
        ----- ---------------
MachinePolicy       Undefined
   UserPolicy       Undefined
      Process       Undefined
  CurrentUser    RemoteSigned
 LocalMachine       Undefined

 インターネットからダウンロードしたファイルのうち、署名がないものはそのまま実行することができないのでスクリプトのロックを解除します。

 Unblock-File -Path $env:USERPROFILE\.pyenv\pyenv-win\bin\pyenv.ps1

「pyenv –version」で再度確認したところ、実行できました。「pyenv install -l」でバージョン一覧を表示し、インストールしたいバージョンをインストールしていきます。

The system cannot find the file specified.
pyenv

FastAPI 0.99.0に対応しているPython 3.11.9をインストールします。

pyenv install 3.11.9

バージョンを確認します。

pyenv versions

現在のPythonのバージョンを確認します。

※Windowsに公式サイトからPython 3.13.2をインストールしていたため、Python 3.13.2が表示されました。

python --version

globalで設定します。

pyenv global 3.11.9

「python –version」で3.11.9に変わっているか確認します。

※確認したところ、先に公式サイトからPython 3.13.2をインストールしていたため、3.13.2のままでした。対応方法は後述するトラブルシューティングで解説します。

ディレクトリを作成

 「Win + R」を押し、「cmd」と入力してコマンドプロンプト開きます。ディレクトリを作成します。作成する場所は任意です。

mkdir backend && cd backend
python -m venv venv  # 仮想環境を作成
venv\Scripts\activate  # 仮想環境を有効化(Windows)

必要なモジュールのインストール

 fastapiとuvicornをインストールします。

pip install fastapi==0.99.0 uvicorn

VScodeをインストール

 必要であればVScode等のエディタを公式サイトからインストールしてください。

実行するコードを用意

main.pyファイルを作成し、以下のコードをコピペします。後程、フロントエンド(React)から呼び出すため、CORS設定を記載しています。CORS設定がないと、ブラウザのセキュリティ機能でブロックされてしまいます。

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# CORS設定
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],  # ReactのURL
    allow_credentials=True,
    allow_methods=["*"],  # すべてのHTTPメソッドを許可
    allow_headers=["*"],  # すべてのヘッダーを許可
)

@app.get("/")
def read_root():
    return {"message": "Hello, FastAPI!"}

FastAPIの実行

 以下のコマンドを実行し、ブラウザで「http://127.0.0.1:8000」にアクセスできることを確認します。

uvicorn main:app --reload

フロントエンドの環境構築

Node.jsをインストール

 公式サイトからNode.jsをインストールします。インストール後、Node.jsのバージョンを確認し、インストールされていることを確認します。

node -v

ディレクトリを作成

 以下のコマンドを順に実行します。ここでは「frontend」というディレクトリを作成し、配下に必要なファイル群が格納されています。

npx create-react-app frontend
cd frontend

Reactの実行

 以下のコマンドを実行します。実行すると「http://localhost:3000」でアプリが自動的に開きます。デフォルトでは、「frontend/src/App.js」の内容が表示されます。

npm start

バックエンド(FastAPI)をAWSのLamdbaに配置+API Gatewayでバックエンド(FastAPI)呼び出し用のURLを作成し、ローカルのフロントエンド(React)から呼び出し

Lambdaにアップロードするフォルダの準備

 main.pyファイルを以下のように変更します。

※CORS設定がない場合、ローカルでReactを実行して「htthp://localhost:3000」を開き、F12で開発者ツールを開く > リロード > Network > Fetch/XHRのstatusで「CORS policy」というエラーを確認することができます。
※「allow_origins=[“http://localhost:3000”]」でよいのですが、CloudFrontでディストリビューションを作成し、Lambdaを呼び出したところ、エラーが出てしまったので「[“*”]」とすべてのドメインを許可しています。後述するトラブルシューティングで解説します。

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from mangum import Mangum

app = FastAPI()

# CORS設定
app.add_middleware(
    CORSMiddleware,
    # allow_origins=["http://localhost:3000"],  # ReactのURL
    allow_origins=["*"],  # 必要に応じて特定のドメインに限定する
    allow_credentials=True,
    allow_methods=["*"],  # すべてのHTTPメソッドを許可
    allow_headers=["*"],  # すべてのヘッダーを許可
)

@app.get("/")
def read_root():
    return {"message": "Hello, AWS Lambda & FastAPI!"}

handler = Mangum(app)  # Lambda のエントリーポイント

 以下のコマンドを実行していきます。

  • 必要なライブラリを「package」フォルダにインストールします。フォルダ名は任意です。
pip install fastapi==0.99.0 mangum --target ./package
  • main.pyをpakageフォルダに追加します。
cp main.py ./package
  • packageフォルダ(必要なライブラリとmain.pyファイル)をzip化
Compress-Archive -Path .\package\* -DestinationPath .\deployment.zip

※再度zip化する場合はコードの末尾にオプションである「-Force」を付けてアーカイブファイルを上書きするようにしてください。

Compress-Archive -Path .\package\* -DestinationPath .\deployment.zip -Force

 上記をすべて実行すると、以下のようなフォルダ構成になっています。「deployment.zip」を後程Lambdaにアップロードします。

fastapi-lambda/
│── package/   ← ここにライブラリがインストールされる
│    ├── fastapi/
│    ├── mangum/
│    ├── ...
│── main.py    ← FastAPIのコード
│── deployment.zip  ← ZIP化して Lambda にアップロード

Lambdaへアップロード

 右上のリージョンがデフォルトでリージョンが「シドニー」になっている場合、「東京」に変更します。

 検索ボックスに「lambda」と検索し、サービスの「Lambda」を選択します。

 右上にある「関数を作成」を押します。

 関数の作成では、以下の設定を行います。

  • ラジオボタンの「一から作成」を選択します。デフォルトで選択されています。
  • 関数名を任意の名前を入力します。
  • ランタイムを「Python 3.11」を選択します。「Python 3.11」を選択している理由は、FastAPIのバージョン0.99.0を使用したいからです。2025年4月1日時点では、最新である「Python 3.13」を選択してLambdaを実行した際にモジュールのエラーが発生してしまいます。
  • アーキテクチャを「x86-64」を選択します。「設定 > システム > バージョン情報」でWindowsのアーキテクチャを確認したところ「x64」であるためローカルのマシン環境と同じものを選択しています。

 上記の設定が終わったら、「関数の作成」を押します。

 コードソースにある「アップロード元 > .zipファイル」を選択します。

 先ほどzip化したフォルダ「deployment.zip」をアップロードから選択し、「保存」を押します。すると、Lambda上で勝手に保存されます。

 画面をスクロールして下の方へ行き、「ランタイム設定」にある「編集」ボタンを押し、「ハンドラ」名を「main.handler」と変更します。

※ハンドラ名とは、どの Python 関数を実行するかを指定するためのエントリーポイントのことです。ここで設定しているハンドラ名は以下の理由から「main.handler」としています。

  • main→ main.py(Lambda にアップロードした Python ファイルの名前)
  • handler→ main.py 内の handler という関数

API Gatewayの作成

検索ボックスで「api」と検索し、サービスの「API Gateway」を選択します。

「APIを作成」を押します。

「HTTP API」の「構築」を押します。

 ここで以下の設定を行い、「次へ」を押します。

  • 「API名」を任意の名前で入力
  • 「IP address type」をIPv4(デフォルト)で選択
  • 「統合を追加」を押し、「統合」のプルダウンから「Lambda」を選択し、「Lambda関数」のプルダウンから先ほど作成したLambda関数名を選択

 ここでは以下の設定を行い、「次へ」を押します。

  • 「メソッド」を「GET」で選択
  • 「リソースパス」を「/」で指定
  • 「統合ターゲット」を先ほど指定して関数名で選択

 ステージ名はデフォルトのまま次へ進み、作成を完了させます。

フロントエンド(React)でAPI Gatewayで発行されたURLを設定

 「App.js」ファイルをデフォルトから以下のように変更します。ここからfetchメソッドのURLを次の手順で変更していきます。

import React, { useEffect, useState } from "react";

function App() {
  const [message, setMessage] = useState("");

  useEffect(() => {
    // fetch("http://127.0.0.1:8000/")
    fetch("https://~.ap-northeast-1.amazonaws.com") // API Gatewayで発行されたURL
      .then((res) => res.json())
      .then((data) => setMessage(data.message));
  }, []);

  return (
    <div>
      <h1>チャットボット</h1>
      <p>APIメッセージ: {message}</p>
    </div>
  );
}

export default App;

 API Gatewayの「Deploy」タブの「Stages」を選択し、「URLを呼び出す」にあるURLをコピーします。このURLを「App.js」ファイルのfetchメソッドのURLに指定します。

ローカルのフロントエンド(React)から呼び出し

 以下のコマンドを実行します。

npm start

 以下のようにバックエンド側(FastAPI)で記載した「return {“message”: “Hello, AWS Lambda & FastAPI!”}」のmessageキーの値である「Hello, AWS Lambda & FastAPI!」がフロントエンド側(React)から表示されていることが確認できました。

S3を使ってフロントエンド(React)を配置+CloudFrontを使ってドメインを作成し、Lambdaに配置したバックエンド(FastAPI)にS3+CloudFrontに配置したフロントエンドから呼び出し

「frontend」フォルダへ移動し、「build」フォルダを作成します。

cd frontend
npm run build

AWS CLIを使ってS3へ「build」フォルダをアップロードします。

AWS CLIのインストール

 AWS CLIはこちらを元にインストールしていきます。Windowsのスタートメニューから「cmd」と検索し、「管理者として実行」を選択します。そして、以下のコマンドを実行します。すると、インストール画面が表示されるので「next」を押してイントールまで操作していきます。

msiexec.exe /i https://awscli.amazonaws.com/AWSCLIV2.msi

 次に、ユーザーの作成やアクセスキーの作成をこちらを参考に行います。

 最後に、以下のコマンドでインストールされたバージョンを確認します。確認したところ「aws-cli/2.25.7 Python/3.12.9 Windows/11 exe/AMD64」がインストールされているようでした。

aws --version

S3バケットの作成

 検索ボックスで「S3」と検索し、サービスの「S3」を選択します。「バケットの作成」を押します。

S3バケット名を入力し、他はすべてデフォルトままにします。その後、「バケットを作成」ボタンを押します。

buildフォルダをアップロード

 以下のコマンドでS3に「build」フォルダ内にあるファイル群をアップロードします。S3無料利用枠があるのでこちらを利用しました。

aws s3 cp build/ s3://<S3バケット名>/ --recursive

CloudFront を設定

 検索ボックスで「CloudFront」と検索し、サービスの「CloudFront」を選択します。「CloudFrontディストリビューションを作成」を押します。その後、以下の設定を行います。

  • 「Origin domain」で先ほど作成したS3バケットを選択します。この時、「名前」が自動的に選択したS3バケット名が入ります。
  • 「オリジンアクセス」に「Origin access control settings (recommended)」を選択します。「Public」を選択した際にうまく画面表示がされませんでした。起きた事象については、後述の「トラブルシューティング:S3バケットのアクセスエラー」で解説します。
  • 「Origin access control」を選択します。最初は選択できるものがないので「Create new OAC」を押して作成します。「名前」と「説明」を入力して「Create」を押して作成します。後程説明しますが、ディストリビューション作成後にS3バケットポリシーを更新する必要があります。
  • 「ウェブアプリケーションファイアウォール(WAF)を有効にします。
  • 「デフォルトルートオブジェクト・オプション」にルート(作成されるディストリビューションドメイン名)でアクセスしたときに表示するファイルを指定します。これが直接的な原因だったかわかりませんが、これを指定しないと「Access Denied」エラーが表示されました。
  •  他の設定はデフォルトのまま「ディストリビューションを作成」を押します。
  • 作成後、以下のように画面が変わります。「ポリシーをコピー」を押し、「S3バケットの権限に移動してポリシーを更新する」を押してS3バケットのポリシーを更新します。
  • 「アクセス許可」タブを押して「バケットポリシー」の「編集」を押します。先ほどコピーしたポリシーを張り付けて保存します。
  • CloudFrontの画面に戻ります。「ディストリビューションドメイン名」をコピーし、ブラウザからアクセスします。「Hello, AWS Lambda & FastAPI!」がフロントエンド(React)で表示できていることが確認できました。App.jsファイルのCORS設定でドメイン名を許可しておかないとエラーが発生します。起きた事象については、後述の「トラブルシューティング:CORSエラー」で解説します。

トラブルシューティング

エラーが出たところについて簡単に解説します。

pyenvで指定したバージョンに変更されない

 原因として、先に公式サイトからPython 3.13.2をインストールしていたため、環境変数のPATHの読み込みでpyenvのパスが後から読み込まれてまうことが原因のようでした。そこで、PATHの先頭に「C:\Users\USER\.pyenv\pyenv-win\shims」を入れるように以下のコマンドで変更しました。

$env:PATH = "$env:USERPROFILE\.pyenv\pyenv-win\shims;" + $env:PATH

結果

PS C:\Users\USER\backend> $env:Path -split ';'
# 変更前
C:\Python313\Scripts\
C:\Python313\
---略---
C:\Users\USER\.pyenv\pyenv-win\shims

# 変更後
C:\Users\USER\.pyenv\pyenv-win\shims
C:\Python313\Scripts\
C:\Python313\
---略---

コマンドプロンプトとPowerShellではPATHの設定が異なるようなのでコマンドプロンプトでも「C:\Users\USER\.pyenv\pyenv-win\shims」をPATHの先頭になるように以下のコマンドで変更します。

set PATH=C:\Users\USER\.pyenv\pyenv-win\shims;%PATH%

S3バケットへのアクセスエラー

ディストリビューションを作成する際、「オリジンアクセス」をデフォルトの「Public」のまま作成してしまうと、ブラウザからアクセスしたときに以下のエラーが発生しました。S3バケットのパーミッション設定が不十分であることによるエラーのようでした。S3にbuildしたReactのファイル群を配置してアクセスする際は「オリジンアクセス」に「Origin access control settings (recommended)」を選択する必要があるようです。

This XML file does not appear to have any style information associated with it. The document tree is shown below.

<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
</Error>

CORSエラー

作成したディストリビューションの「ディストリビューションドメイン名」をコピーし、ブラウザからアクセスします。フロントの画面は正常に表示されました。しかし、バックエンド(FastAPI)のメッセージが取得できていませんでした。原因は、バックエンドのmain.pyファイルでCORS設定の「allow_origins」をローカルドメインのみに設定していたためでした。開発者ツールで確認すると「CORS error」が発生していることが確認できました。そこでmain.pyファイルの「allow_origins=[“http://localhost:3000”]」の設定をすべてのドメインを許可するように「allow_origins=[“*”]」と変更しました。以下はmain.pyファイルを変更したコードになります。

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from mangum import Mangum

app = FastAPI()

# CORS設定
app.add_middleware(
    CORSMiddleware,
    # allow_origins=["http://localhost:3000"],  # ReactのURL
    allow_origins=["*"],  # 必要に応じて特定のドメインに限定する
    allow_credentials=True,
    allow_methods=["*"],  # すべてのHTTPメソッドを許可
    allow_headers=["*"],  # すべてのヘッダーを許可
)

@app.get("/")
def read_root():
    return {"message": "Hello, AWS Lambda & FastAPI!"}

handler = Mangum(app)

覚えておきたいコマンド

仮想環境構築

python -m venv [仮想環境名]

フォルダの削除

rmdir /s /q フォルダ名

コメント

タイトルとURLをコピーしました