Skip to content

FastAPI

Filter this guidePick generator option values to hide sections that don't apply.

FastAPIは、PythonでAPIを構築するためのフレームワークです。

FastAPIジェネレーターは、AWS CDKまたはTerraformインフラストラクチャのセットアップを含む新しいFastAPIを作成します。生成されたバックエンドは、サーバーレスデプロイメントにAWS Lambdaを使用し、AWS API Gateway APIを介して公開されます。観測性のためにAWS Lambda Powertoolsをセットアップし、ロギング、AWS X-Rayトレーシング、Cloudwatch Metricsを含みます。

新しいFastAPIは2つの方法で生成できます:

Terminal window
pnpm nx g @aws/nx-plugin:py#api --framework=fastapi
変更されるファイルを確認するためにドライランを実行することもできます
Terminal window
pnpm nx g @aws/nx-plugin:py#api --framework=fastapi --dry-run
パラメータデフォルト説明
name 必須string-生成するAPIプロジェクトの名前
framework fastapifastapi使用するAPIフレームワーク。
integrationPattern isolated | sharedisolatedAPI用にAPI Gateway統合を生成する方法。isolated(デフォルト)またはsharedから選択します。
auth iam | cognito | customiamAPIの認証に使用する方法。iam(デフォルト)、cognito、customから選択します。
directory stringpackagesアプリケーションを保存するディレクトリ
subDirectory string-プロジェクトが配置されるサブディレクトリ。デフォルトではプロジェクト名になります。
iac inherit | cdk | terraforminherit優先するIaCプロバイダー。デフォルトでは、初期選択から継承されます。
moduleName string-Pythonモジュール名
infra rest-lambda | http-lambda | nonerest-lambdaこのAPIをデプロイするために使用するインフラストラクチャのタイプ。
preferInstallDependencies booleantrueジェネレーター実行後に依存関係のインストールを優先するかどうか。複数のジェネレーターをバッチ処理する際にインストールを延期する場合はfalseに設定します(後続のジェネレーターがNxプロジェクトグラフを計算できるよう、必要に応じてインストールは実行されます)。最後に一度だけインストールします。

ジェネレーターは<directory>/<api-name>ディレクトリに以下のプロジェクト構造を作成します:

  • project.json プロジェクト設定とビルドターゲット
  • pyproject.toml Pythonプロジェクト設定と依存関係
  • run.sh uvicorn経由でFastAPIアプリを起動するLambda Web Adapterブートストラップスクリプト
  • Directory<module_name>
    • __init__.py モジュール初期化
    • init.py FastAPIアプリをセットアップし、powertoolsミドルウェアを設定
    • main.py API実装
  • Directoryscripts
    • generate_open_api.py FastAPIアプリからOpenAPIスキーマを生成するスクリプト

このジェネレーターは、選択した iac に基づいてインフラストラクチャをコードとして提供するため、関連する CDK コンストラクトまたは Terraform モジュールを含む packages/common にプロジェクトを作成します。

共通のインフラストラクチャコードプロジェクトは、次のように構成されています:

  • Directorypackages/common/constructs
    • Directorysrc
      • Directoryapp/ プロジェクト/ジェネレーター固有のインフラストラクチャ用のコンストラクト
      • Directorycore/ app 内のコンストラクトによって再利用される汎用コンストラクト
      • index.ts app からコンストラクトをエクスポートするエントリーポイント
    • project.json プロジェクトのビルドターゲットと設定

APIをデプロイするために、以下のファイルが生成されます:

  • Directorypackages/common/constructs/src
    • Directoryapp
      • Directoryapis
        • <project-name>.ts CDK construct for deploying your API
    • Directorycore
      • Directoryapi
        • http-api.ts CDK construct for deploying an HTTP API (if you selected to deploy an HTTP API)
        • rest-api.ts CDK construct for deploying a REST API (if you selected to deploy a REST API)
        • utils.ts Utilities for the API constructs

デプロイされたアプリケーションは以下のアーキテクチャを持ちます:

ClientWAFAPI Gateway(REST API)LambdaCloudWatch(Logs, Metrics)X-Ray(Traces)

REST APIには、API Gatewayステージの前にAWS WAFv2 Web ACLが含まれており、AWSマネージドのデフォルトルールセットが有効になっています。

メインのAPI実装はmain.pyにあります。ここでAPIルートとその実装を定義します。以下は例です:

from pydantic import BaseModel
from .init import app, tracer
class Item(BaseModel):
name: str
@app.get("/items/{item_id}")
@tracer.capture_method
def get_item(item_id: int) -> Item:
return Item(name=...)
@app.post("/items")
@tracer.capture_method
def create_item(item: Item):
return ...

ジェネレーターは以下の機能を自動的にセットアップします:

  1. 観測性のためのAWS Lambda Powertools統合
  2. エラーハンドリングミドルウェア
  3. リクエスト/レスポンスの相関
  4. メトリクス収集
  5. Lambda Web Adapterとuvicornを介したAWS Lambdaデプロイメント
  6. 型安全なストリーミング(REST APIのみ)

ジェネレーターはAWS Lambda Powertoolsを使用して構造化ロギングを設定します。ルートハンドラーでロガーにアクセスできます:

from .init import app, logger
@app.get("/items/{item_id}")
def read_item(item_id: int):
logger.info("Fetching item", extra={"item_id": item_id})
return {"item_id": item_id}

ロガーは自動的に以下を含みます:

  • リクエストトレーシングのための相関ID
  • リクエストパス、マッチしたルート、メソッド

AWS X-Rayトレーシングは自動的に設定されます。トレースにカスタムサブセグメントを追加できます:

from .init import app, tracer
@app.get("/items/{item_id}")
@tracer.capture_method
def read_item(item_id: int):
# Creates a new subsegment
with tracer.provider.in_subsegment("fetch-item-details"):
# Your logic here
return {"item_id": item_id}

CloudWatchメトリクスは各リクエストに対して自動的に収集されます。カスタムメトリクスを追加できます:

from .init import app, metrics
from aws_lambda_powertools.metrics import MetricUnit
@app.get("/items/{item_id}")
def read_item(item_id: int):
metrics.add_metric(name="ItemViewed", unit=MetricUnit.Count, value=1)
return {"item_id": item_id}

デフォルトのメトリクスには以下が含まれます:

  • リクエスト数
  • 成功/失敗数
  • ルートごとのメトリクス(<method> <path>routeディメンション経由)

ジェネレーターには包括的なエラーハンドリングが含まれています:

from fastapi import HTTPException
@app.get("/items/{item_id}")
def read_item(item_id: int):
if item_id < 0:
raise HTTPException(status_code=400, detail="Item ID must be positive")
return {"item_id": item_id}

未処理の例外はミドルウェアによってキャッチされ、以下を行います:

  1. スタックトレースを含む完全な例外をログに記録
  2. 失敗メトリクスを記録
  3. クライアントに安全な500レスポンスを返す
  4. 相関IDを保持

呼び出し元ユーザーへのアクセス

Section titled “呼び出し元ユーザーへのアクセス”

APIが認証によって保護されている場合、ルートハンドラーは誰が呼び出しているかを知る必要があることがよくあります。生成されたFastAPIはLambda Web Adapterを介してAWS Lambda内で実行され、API Gatewayリクエストコンテキストをx-amzn-request-contextヘッダーにJSONとして転送します。FastAPIのRequestから読み取って、呼び出し元のIDを抽出できます。

例として、呼び出し元ユーザーの詳細を返す/meエンドポイントを追加しましょう。抽出をFastAPI依存関係として実装し、ルート間で再利用できるようにします。リクエストコンテキストの形状、したがってIDの抽出方法は、選択したauthメソッドとREST APIまたはHTTP APIのどちらをデプロイしたかによって異なります。

auth = iam

IAM認証の場合、API Gatewayリクエストコンテキストから抽出されたsubを使用してCognitoで呼び出し元を検索します。main.pyと同じ場所にidentity.pyを作成します:

import json
import os
from typing import Annotated
from boto3 import client
from fastapi import Depends, HTTPException, Request
from pydantic import BaseModel
cognito = client("cognito-idp")
class Identity(BaseModel):
sub: str
username: str
def get_identity(request: Request) -> Identity:
# The Lambda Web Adapter forwards the API Gateway request context as JSON
request_context_header = request.headers.get("x-amzn-request-context")
if not request_context_header:
raise HTTPException(status_code=403, detail="Unable to determine calling user")
request_context = json.loads(request_context_header)
provider = request_context.get("identity", {}).get("cognitoAuthenticationProvider")
sub = provider.split(":")[-1] if provider else None
if not sub:
raise HTTPException(status_code=403, detail="Unable to determine calling user")
users = cognito.list_users(
# Assumes user pool id is configured in lambda environment
UserPoolId=os.environ["USER_POOL_ID"],
Limit=1,
Filter=f'sub="{sub}"',
).get("Users", [])
if len(users) != 1:
raise HTTPException(status_code=403, detail=f"No user found with subjectId {sub}")
return Identity(sub=sub, username=users[0]["Username"])
CurrentUser = Annotated[Identity, Depends(get_identity)]
auth = cognito

auth: 'cognito'の場合、API Gateway Cognito User Poolsオーソライザーは、呼び出し元がAuthorizationヘッダーで提供するJWTを検証し、検証されたクレームをリクエストコンテキストに配置します。

main.pyと同じ場所にidentity.pyを作成します:

import json
from typing import Annotated
from fastapi import Depends, HTTPException, Request
from pydantic import BaseModel
class Identity(BaseModel):
sub: str
username: str
def get_identity(request: Request) -> Identity:
# The Lambda Web Adapter forwards the API Gateway request context as JSON
request_context_header = request.headers.get("x-amzn-request-context")
if not request_context_header:
raise HTTPException(status_code=403, detail="Unable to determine calling user")
request_context = json.loads(request_context_header)
claims = request_context.get("authorizer", {}).get("claims", {})
sub = claims.get("sub")
username = claims.get("username")
if not sub or not username:
raise HTTPException(status_code=403, detail="Unable to determine calling user")
return Identity(sub=sub, username=username)
CurrentUser = Annotated[Identity, Depends(get_identity)]

その後、呼び出し元のIDが必要な任意のルートにCurrentUser依存関係を注入できます:

from .identity import CurrentUser, Identity
from .init import app, tracer
@app.get("/me")
@tracer.capture_method
def me(identity: CurrentUser) -> Identity:
return identity
infra = rest-lambda

生成されたFastAPIは、REST APIを使用する場合、すぐにストリーミングレスポンスをサポートします。インフラストラクチャは、AWS Lambda Web Adapterを使用してLambda内でuvicorn経由でFastAPIを実行するように設定されており、すべてのREST API操作に対してAPI GatewayでResponseTransferMode.STREAMを使用することで、ストリーミング操作と非ストリーミング操作を並行して動作させることができます。

生成されたinit.pyは、適切なOpenAPIスキーマ生成を伴う型安全なストリーミングを提供するJsonStreamingResponseクラスをエクスポートします。これにより、connectionジェネレーターが正しく型付けされたストリーミングクライアントメソッドを生成できます。

from pydantic import BaseModel
from .init import app, JsonStreamingResponse
class Chunk(BaseModel):
message: str
async def generate_chunks():
for i in range(100):
yield Chunk(message=f"This is chunk {i}")
@app.post(
"/stream",
response_class=JsonStreamingResponse,
responses={200: JsonStreamingResponse.openapi_response(Chunk, "Stream of chunks")},
)
async def my_stream() -> JsonStreamingResponse:
return JsonStreamingResponse(generate_chunks())

JsonStreamingResponseクラスは:

  1. PydanticモデルをJSON Lines形式(application/jsonl)にシリアライズ
  2. itemSchemaを含む正しいOpenAPIスキーマを生成するopenapi_responseヘルパーを提供し、connectionジェネレーターが型安全なストリーミングクライアントメソッドを生成できるようにします

レスポンスのストリームを消費するには、connectionジェネレーターを使用できます。これにより、ストリーミングされたチャンクを反復処理するための型安全なメソッドが提供されます。

FastAPIジェネレーターは、選択したiacに基づいてCDKまたはTerraformのインフラストラクチャコードを作成します。これを使用してFastAPIをデプロイできます。

common/constructsフォルダーにAPIをデプロイするためのCDKコンストラクトがあります。これをCDKアプリケーションで使用できます:

import { MyApi } from '@my-scope/common-constructs';
export class ExampleStack extends Stack {
constructor(scope: Construct, id: string) {
// Add the api to your stack
const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});
}
}

これにより以下がセットアップされます:

  1. FastAPIアプリケーションの各操作用のAWS Lambda関数
  2. 関数トリガーとしてのAPI Gateway HTTP/REST API
  3. IAMロールと権限
  4. CloudWatchロググループ
  5. X-Rayトレーシング設定
  6. CloudWatchメトリクス名前空間
auth = cognito
auth = custom
infra = rest-lambda

REST API の場合、生成されたコンストラクトはデフォルトで AWS WAFv2 Web ACL を API Gateway ステージに関連付けます。Web ACL は AWS マネージド型デフォルトルールセット(AWSManagedRulesCommonRuleSet および AWSManagedRulesKnownBadInputsRuleSet)を使用し、OWASP Top 10 を含む一般的な Web エクスプロイトに対する保護を提供します。WAF リクエストログは CloudWatch Logs グループに書き込まれます。

生成された rest-api コンストラクトを編集して、ルールを追加、削除、または調整できます(例えば、レートベースルールや追加のマネージド型ルールグループを追加するなど)。

オプトアウトするには(例えば、独自の Web ACL をアタッチする場合)、enableWaffalse に設定します:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
enableWaf: false,
});
infra = rest-lambda

REST APIの場合、生成されたインフラストラクチャはデフォルトでアクセスログを有効にし、リクエストごとに1行の構造化JSONを専用のCloudWatch Logsグループに書き込みます。ログループは顧客管理のKMSキーで暗号化され、1年間保持されます。

API Gatewayは、アカウントレベルのCloudWatch Logsロールを使用してアクセスログを書き込みます。このロールはAWS::ApiGateway::Account設定で構成されており、これはリージョンごと、アカウントごとのシングルトンです。つまり、リージョン内のすべてのREST APIに対して1つのロールしか存在しません。独立してデプロイされる複数のスタック間でこれを安全に管理するため、生成されたインフラストラクチャは次のようになっています:

  • 共有CloudWatch Logsロールを作成し、動作中のロールがまだ設定されていない場合にのみアカウントに構成するため、デプロイメントが他のスタックが所有するロールを上書きすることはありません。
  • ティアダウン時にアカウント設定をそのままにするため、1つのスタックを破棄してもリージョン内の他のREST APIのログ記録が無効になることはありません。

アカウントロールはApiGatewayAccountコンストラクトによって管理されます。これはApiGatewayAccount.ensure(scope)を介して解決されるスタックスコープのシングルトンです。各REST APIのステージはこれに依存しており、ロールはLambdaバックのカスタムリソースによって構成されます。

アクセスログフォーマットは、APIが拡張するRestApiコンストラクトによって設定されます。カスタマイズするには、生成されたpackages/common/constructs/src/app/apis/my-api.ts内でdeployOptionssuperに渡し、コンストラクトがすでに設定しているtracingEnabledを保持します:

packages/common/constructs/src/app/apis/my-api.ts
super(scope, id, {
apiName: 'MyApi',
// ...
deployOptions: {
tracingEnabled: true,
accessLogFormat: AccessLogFormat.clf(),
},
...props,
});

AccessLogFormataws-cdk-lib/aws-apigatewayからインポートされます。設定しないものはすべて、コンストラクトのデフォルト(標準フィールドを持つJSON形式)を保持します。

REST/HTTP API CDK コンストラクトは、各オペレーションのインテグレーションを定義するための型安全なインターフェースを提供するように構成されています。

CDK コンストラクトは、以下に説明する完全な型安全インテグレーションサポートを提供します。

デフォルトインテグレーション

Section titled “デフォルトインテグレーション”

静的な defaultIntegrations を使用して、各オペレーションに個別の AWS Lambda 関数を定義するデフォルトパターンを利用できます:

new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});

インテグレーションへのアクセス

Section titled “インテグレーションへのアクセス”

API コンストラクトの integrations プロパティを介して、基礎となる AWS Lambda 関数に型安全な方法でアクセスできます。例えば、API が sayHello という名前のオペレーションを定義していて、この関数にいくつかの権限を追加する必要がある場合、次のように実行できます:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});
// sayHello is typed to the operations defined in your API
api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({
effect: Effect.ALLOW,
actions: [...],
resources: [...],
}));

API が shared パターンを使用している場合、共有ルーター Lambda は api.integrations.$router として公開されます:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});
api.integrations.$router.handler.addEnvironment('LOG_LEVEL', 'DEBUG');

デフォルトオプションのカスタマイズ

Section titled “デフォルトオプションのカスタマイズ”

各デフォルトインテグレーションの Lambda 関数を作成する際に使用されるオプションをカスタマイズしたい場合は、withDefaultOptions メソッドを使用できます。例えば、すべての Lambda 関数を Vpc 内に配置したい場合:

const vpc = new Vpc(this, 'Vpc', ...);
new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withDefaultOptions({
vpc,
})
.build(),
});

オペレーションごとのオプションのカスタマイズ

Section titled “オペレーションごとのオプションのカスタマイズ”

_特定の_オペレーションのデフォルトインテグレーションを作成するために使用されるオプションをカスタマイズする(他のオペレーションに影響を与えずに)には、withOperationOptions メソッドを使用できます。例えば、1 つのオペレーションだけの Lambda 関数タイムアウトを増やしたい場合:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOperationOptions({
sayHello: {
timeout: Duration.seconds(60),
},
})
.build(),
});
// The selected operations remain default integrations, so they're still typed accordingly:
api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({ ... }));

指定したオプションは、デフォルトインテグレーションオプション(および withDefaultOptions で設定されたオプション)とマージされます。withOverrides で置き換えたオペレーションにはオプションを指定できないことに注意してください。これらはデフォルトインテグレーションを使用しなくなるためです。

withOperationOptionswithOverrides の両方で同じオペレーションをターゲットにすると、呼び出す順序に関係なく型エラーが発生します。

インテグレーションのオーバーライド

Section titled “インテグレーションのオーバーライド”

withOverrides メソッドを使用して、特定のオペレーションのインテグレーションをオーバーライドすることもできます。各オーバーライドは、HTTP または REST API の適切な CDK インテグレーションコンストラクトに型付けされた integration プロパティを指定する必要があります。withOverrides メソッドも型安全です。例えば、getDocumentation API をオーバーライドして、外部ウェブサイトでホストされているドキュメントを指すようにしたい場合、次のように実現できます:

new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOverrides({
getDocumentation: {
integration: new HttpIntegration('https://example.com/documentation'),
},
})
.build(),
});

また、オーバーライドされたインテグレーションは、api.integrations.getDocumentation を介してアクセスする際に handler プロパティを持たなくなることに気付くでしょう。

インテグレーションに追加のプロパティを追加することもでき、それらも適切に型付けされます。これにより、他のタイプのインテグレーションを抽象化しながら型安全性を維持できます。例えば、REST API 用の S3 インテグレーションを作成し、後で特定のオペレーションのバケットを参照したい場合、次のように実行できます:

const storageBucket = new Bucket(this, 'Bucket', { ... });
const apiGatewayRole = new Role(this, 'ApiGatewayS3Role', {
assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),
});
storageBucket.grantRead(apiGatewayRole);
const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOverrides({
getFile: {
bucket: storageBucket,
integration: new AwsIntegration({
service: 's3',
integrationHttpMethod: 'GET',
path: `${storageBucket.bucketName}/{fileName}`,
options: {
credentialsRole: apiGatewayRole,
requestParameters: {
'integration.request.path.fileName': 'method.request.querystring.fileName',
},
integrationResponses: [{ statusCode: '200' }],
},
}),
options: {
requestParameters: {
'method.request.querystring.fileName': true,
},
methodResponses: [{
statusCode: '200',
}],
}
},
})
.build(),
});
// Later, perhaps in another file, you can access the bucket property we defined
// in a type-safe manner
api.integrations.getFile.bucket.grantRead(...);

オーソライザーのオーバーライド

Section titled “オーソライザーのオーバーライド”

インテグレーションで options を指定して、オーソライザーなどの特定のメソッドオプションをオーバーライドすることもできます。例えば、getDocumentation オペレーションに Cognito 認証を使用したい場合:

new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOverrides({
getDocumentation: {
integration: new HttpIntegration('https://example.com/documentation'),
options: {
authorizer: new CognitoUserPoolsAuthorizer(...) // for REST, or HttpUserPoolAuthorizer for an HTTP API
}
},
})
.build(),
});

必要に応じて、デフォルトインテグレーションを使用せず、各オペレーションに直接インテグレーションを提供することもできます。これは、例えば各オペレーションが異なるタイプのインテグレーションを使用する必要がある場合や、新しいオペレーションを追加する際に型エラーを受け取りたい場合に便利です:

new MyApi(this, 'MyApi', {
integrations: {
sayHello: {
integration: new LambdaIntegration(...),
},
getDocumentation: {
integration: new HttpIntegration(...),
},
},
});

生成された CDK API コンストラクトは、2 つのインテグレーションパターンをサポートしています:

  • isolated は、オペレーションごとに 1 つの Lambda 関数を作成します。これは生成された API のデフォルトです。
  • shared は、単一のデフォルトルーター Lambda を作成し、特定のインテグレーションをオーバーライドしない限り、すべてのオペレーションでそれを再利用します。

isolated は、オペレーションごとにより細かい権限と設定を提供します。shared は、選択的なオーバーライドを許可しながら、Lambda と API Gateway インテグレーションの拡散を削減します。

例えば、pattern'shared' に設定すると、オペレーションごとに 1 つではなく、単一の関数が作成されます:

packages/common/constructs/src/app/apis/my-api.ts
export class MyApi<...> extends ... {
public static defaultIntegrations = (scope: Construct) => {
...
return IntegrationBuilder.rest({
pattern: 'shared',
...
});
};
}

FastAPIの操作はPythonで定義され、CDKインフラストラクチャはTypeScriptで定義されるため、CDKコンストラクトにメタデータを提供して統合のための型安全なインターフェースを提供するためにコード生成を実装します。

generate:<ApiName>-metadataターゲットが共通コンストラクトのproject.jsonに追加され、このコード生成を容易にします。これはpackages/common/constructs/src/generated/my-api/metadata.gen.tsのようなファイルを出力します。これはビルド時に生成されるため、バージョン管理では無視されます。

auth = iam

IAM認証を使用することを選択した場合、grantInvokeAccessメソッドを使用してAPIへのアクセスを付与できます:

api.grantInvokeAccess(myIdentityPool.authenticatedRole);

ジェネレーターは、以下のコマンドで実行できるローカル開発サーバーを設定します:

Terminal window
pnpm nx serve my-api

これにより、以下を備えたローカルFastAPI開発サーバーが起動します:

  • コード変更時の自動リロード
  • /docsまたは/redocでのインタラクティブなAPIドキュメント
  • /openapi.jsonでのOpenAPIスキーマ

ReactウェブサイトからAPIを呼び出すには、connectionジェネレーターを使用できます。

connectionジェネレーターを使用して、このプロジェクトをワークスペース内の他のプロジェクトと統合します。このプロジェクトに関連する以下の接続があります:

FastAPI
React to FastAPIReactウェブサイトからPython FastAPIを呼び出す
FastAPIAmazon DynamoDBPython
FastAPI to Python DynamoDBFastAPIをDynamoDBテーブルに接続