FastAPI
FastAPI はPythonでAPIを構築するためのフレームワークです。
FastAPIジェネレータは、AWS CDKまたはTerraformのインフラストラクチャ設定を含む新しいFastAPIプロジェクトを作成します。生成されるバックエンドはサーバーレスデプロイのためAWS Lambdaを使用し、AWS API Gateway APIを通じて公開されます。AWS Lambda Powertools を設定し、ロギング、AWS X-Rayトレーシング、CloudWatchメトリクスを含むオブザーバビリティ機能を有効化します。
FastAPIの生成
Section titled “FastAPIの生成”新しいFastAPIプロジェクトを2つの方法で生成できます:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)
"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - py#fast-api
- 必須パラメータを入力
- クリック
Generate
pnpm nx g @aws/nx-plugin:py#fast-api
yarn nx g @aws/nx-plugin:py#fast-api
npx nx g @aws/nx-plugin:py#fast-api
bunx nx g @aws/nx-plugin:py#fast-api
パラメータ | 型 | デフォルト | 説明 |
---|---|---|---|
name 必須 | string | - | Name of the API project to generate |
computeType | string | ServerlessApiGatewayRestApi | The type of compute to use to deploy this API. Choose between ServerlessApiGatewayRestApi (default) or ServerlessApiGatewayHttpApi. |
auth | string | IAM | The method used to authenticate with your API. Choose between IAM (default), Cognito or None. |
directory | string | packages | The directory to store the application in. |
iacProvider | string | CDK | The preferred IaC provider |
moduleName | string | - | Python module name |
ジェネレータの出力
Section titled “ジェネレータの出力”ジェネレータは <directory>/<api-name>
ディレクトリに以下のプロジェクト構造を作成します:
- project.json プロジェクト設定とビルドターゲット
- pyproject.toml Pythonプロジェクト設定と依存関係
Directory<module_name>
- __init__.py モジュール初期化
- init.py FastAPIアプリのセットアップとPowertoolsミドルウェアの設定
- main.py API実装
Directoryscripts
- generate_open_api.py FastAPIアプリからOpenAPIスキーマを生成するスクリプト
インフラストラクチャ
Section titled “インフラストラクチャ”このジェネレータは選択した iacProvider
に基づいてInfrastructure as Codeを生成するため、packages/common
に関連するCDKコンストラクトまたはTerraformモジュールを含むプロジェクトを作成します。
共通のInfrastructure as Codeプロジェクトは以下の構造を持ちます:
Directorypackages/common/constructs
Directorysrc
Directoryapp/ プロジェクト/ジェネレータ固有のインフラストラクチャ用コンストラクト
- …
Directorycore/
app
内のコンストラクトで再利用される汎用コンストラクト- …
- index.ts
app
からコンストラクトをエクスポートするエントリーポイント
- project.json プロジェクトのビルドターゲットと設定
Directorypackages/common/terraform
Directorysrc
Directoryapp/ プロジェクト/ジェネレータ固有のインフラストラクチャ用Terraformモジュール
- …
Directorycore/
app
内のモジュールで再利用される汎用モジュール- …
- project.json プロジェクトのビルドターゲットと設定
APIをデプロイするために、以下のファイルが生成されます:
Directorypackages/common/constructs/src
Directoryapp
Directoryapis
- <project-name>.ts APIをデプロイするためのCDKコンストラクト
Directorycore
Directoryapi
- http-api.ts HTTP APIをデプロイするCDKコンストラクト(HTTP APIをデプロイする選択をした場合)
- rest-api.ts REST APIをデプロイするCDKコンストラクト(REST APIをデプロイする選択をした場合)
- utils.ts APIコンストラクト用のユーティリティ
Directorypackages/common/terraform/src
Directoryapp
Directoryapis
Directory<project-name>
- <project-name>.tf APIをデプロイするためのモジュール
Directorycore
Directoryapi
Directoryhttp-api
- http-api.tf HTTP APIをデプロイするモジュール(HTTP APIをデプロイする選択をした場合)
Directoryrest-api
- rest-api.tf REST APIをデプロイするモジュール(REST APIをデプロイする選択をした場合)
FastAPIの実装
Section titled “FastAPIの実装”メインのAPI実装は main.py
に記述します。ここでAPIルートとその実装を定義します。例:
from .init import app, tracerfrom pydantic import BaseModel
class Item(BaseModel): name: str
@app.get("/items/{item_id}")def get_item(item_id: int) -> Item: return Item(name=...)
@app.post("/items")def create_item(item: Item): return ...
ジェネレータが自動設定する機能:
- オブザーバビリティのためのAWS Lambda Powertools統合
- エラーハンドリングミドルウェア
- リクエスト/レスポンス相関ID
- メトリクス収集
- Mangumを使用したAWS Lambdaハンドラ
AWS Lambda Powertoolsによるオブザーバビリティ
Section titled “AWS Lambda Powertoolsによるオブザーバビリティ”構造化ロギングを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
- リクエストパスとメソッド
- Lambdaコンテキスト情報
- コールドスタート指標
トレーシング
Section titled “トレーシング”AWS X-Rayトレーシングを自動設定。カスタムサブセグメントを追加可能:
from .init import app, tracer
@app.get("/items/{item_id}")@tracer.capture_methoddef read_item(item_id: int): with tracer.provider.in_subsegment("fetch-item-details"): return {"item_id": item_id}
リクエスト毎にCloudWatchメトリクスを自動収集。カスタムメトリクス追加可能:
from .init import app, metricsfrom 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}
デフォルトメトリクス:
- リクエスト数
- 成功/失敗数
- コールドスタートメトリクス
- ルート別メトリクス
エラーハンドリング
Section titled “エラーハンドリング”包括的なエラーハンドリングを実装:
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}
未処理例外はミドルウェアで捕捉され:
- スタックトレース付きで例外を記録
- 失敗メトリクスを記録
- クライアントに安全な500レスポンスを返却
- 相関IDを保持
ストリーミング
Section titled “ストリーミング”FastAPIでは StreamingResponse
レスポンスタイプでストリーミング応答を実装可能
インフラストラクチャ変更
Section titled “インフラストラクチャ変更”AWS API Gatewayはストリーミング応答をサポートしないため、AWS Lambda Function URLを使用する必要があります
生成された common/constructs/src/app/apis/<name>-api.ts
コンストラクトをFunction URLデプロイ用に置換
ストリーミングFunctionURLコンストラクト例
import { Duration, Stack, CfnOutput } from 'aws-cdk-lib';import { IGrantable, Grant } from 'aws-cdk-lib/aws-iam';import { Runtime, Code, Tracing, LayerVersion, FunctionUrlAuthType, InvokeMode, Function,} from 'aws-cdk-lib/aws-lambda';import { Construct } from 'constructs';import url from 'url';import { RuntimeConfig } from '../../core/runtime-config.js';
export class MyApi extends Construct { public readonly handler: Function;
constructor(scope: Construct, id: string) { super(scope, id);
this.handler = new Function(this, 'Handler', { runtime: Runtime.PYTHON_3_12, handler: 'run.sh', code: Code.fromAsset( url.fileURLToPath( new URL( '../../../../../../dist/packages/my_api/bundle', import.meta.url, ), ), ), timeout: Duration.seconds(30), tracing: Tracing.ACTIVE, environment: { AWS_CONNECTION_REUSE_ENABLED: '1', }, });
const stack = Stack.of(this); this.handler.addLayers( LayerVersion.fromLayerVersionArn( this, 'LWALayer', `arn:aws:lambda:${stack.region}:753240598075:layer:LambdaAdapterLayerX86:24`, ), ); this.handler.addEnvironment('PORT', '8000'); this.handler.addEnvironment('AWS_LWA_INVOKE_MODE', 'response_stream'); this.handler.addEnvironment('AWS_LAMBDA_EXEC_WRAPPER', '/opt/bootstrap'); const functionUrl = this.handler.addFunctionUrl({ authType: FunctionUrlAuthType.AWS_IAM, invokeMode: InvokeMode.RESPONSE_STREAM, cors: { allowedOrigins: ['*'], allowedHeaders: [ 'authorization', 'content-type', 'x-amz-content-sha256', 'x-amz-date', 'x-amz-security-token', ], }, });
new CfnOutput(this, 'MyApiUrl', { value: functionUrl.url });
RuntimeConfig.ensure(this).config.apis = { ...RuntimeConfig.ensure(this).config.apis!, MyApi: functionUrl.url, }; }
public grantInvokeAccess(grantee: IGrantable) { Grant.addToPrincipal({ grantee, actions: ['lambda:InvokeFunctionUrl'], resourceArns: [this.handler.functionArn], conditions: { StringEquals: { 'lambda:FunctionUrlAuthType': 'AWS_IAM', }, }, }); }}
API Gatewayインフラをストリーミング対応Lambda Function URLに置換
ストリーミングLambda Function URL設定例
data "aws_caller_identity" "current" {}data "aws_region" "current" {}
resource "aws_lambda_function" "my_api_handler" { filename = "../../../../../../dist/packages/my_api/bundle.zip" function_name = "my-api-handler" role = aws_iam_role.lambda_execution_role.arn handler = "run.sh" runtime = "python3.12" timeout = 30 source_code_hash = filebase64sha256("../../../../../../dist/packages/my_api/bundle.zip")
tracing_config { mode = "Active" }
environment { variables = { AWS_CONNECTION_REUSE_ENABLED = "1" PORT = "8000" AWS_LWA_INVOKE_MODE = "response_stream" AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap" } }
layers = [ "arn:aws:lambda:${data.aws_region.current.name}:753240598075:layer:LambdaAdapterLayerX86:24" ]
depends_on = [ aws_iam_role_policy_attachment.lambda_logs, aws_cloudwatch_log_group.lambda_logs, ]}
resource "aws_cloudwatch_log_group" "lambda_logs" { name = "/aws/lambda/my-api-handler" retention_in_days = 14}
resource "aws_iam_role" "lambda_execution_role" { name = "my-api-lambda-execution-role"
assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } } ] })}
resource "aws_iam_role_policy_attachment" "lambda_logs" { role = aws_iam_role.lambda_execution_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"}
resource "aws_iam_role_policy_attachment" "lambda_xray" { role = aws_iam_role.lambda_execution_role.name policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"}
resource "aws_lambda_function_url" "my_api_url" { function_name = aws_lambda_function.my_api_handler.function_name authorization_type = "AWS_IAM" invoke_mode = "RESPONSE_STREAM"
cors { allow_credentials = false allow_origins = ["*"] allow_methods = ["*"] allow_headers = [ "authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token" ] expose_headers = ["date", "keep-alive"] max_age = 86400 }}
output "my_api_url" { description = "URL for the streaming FastAPI Lambda Function" value = aws_lambda_function_url.my_api_url.function_url}
resource "aws_ssm_parameter" "my_api_url" { name = "/runtime-config/apis/MyApi" type = "String" value = aws_lambda_function_url.my_api_url.function_url
tags = { Environment = "production" Service = "my-api" }}
resource "aws_iam_policy" "my_api_invoke_policy" { name = "my-api-invoke-policy" description = "Policy to allow invoking the streaming FastAPI Lambda Function URL"
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = "lambda:InvokeFunctionUrl" Resource = aws_lambda_function.my_api_handler.arn Condition = { StringEquals = { "lambda:FunctionUrlAuthType" = "AWS_IAM" } } } ] })}
ストリーミング対応インフラ更新後、FastAPIでストリーミングAPIを実装:
StreamingResponse
を返却- 各レスポンスチャンクの型を宣言
- API接続 使用時はOpenAPI拡張
x-streaming: true
を追加
JSONオブジェクトをストリーミングする例:
from pydantic import BaseModelfrom fastapi.responses import StreamingResponse
class Chunk(BaseModel): message: str timestamp: datetime
async def stream_chunks(): for i in range(0, 100): yield Chunk(message=f"This is chunk {i}", timestamp=datetime.now())
@app.get("/stream", openapi_extra={'x-streaming': True})def my_stream() -> Chunk: return StreamingResponse(stream_chunks(), media_type="application/json")
ストリームレスポンスを消費するには API接続ジェネレータ を使用
FastAPIのデプロイ
Section titled “FastAPIのデプロイ”iacProvider
選択に基づきCDK/Terraformインフラコードを生成。これを使用してデプロイ可能
common/constructs
フォルダのCDKコンストラクトを使用:
import { MyApi } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(), }); }}
設定内容:
- FastAPI操作毎のLambda関数
- API Gateway HTTP/REST APIトリガー
- IAMロールと権限
- CloudWatchロググループ
- X-Rayトレーシング設定
- CloudWatchメトリクスネームスペース
common/terraform
フォルダのモジュールを使用:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
env = { ENVIRONMENT = var.environment LOG_LEVEL = "INFO" }
additional_iam_policy_statements = [ # 追加権限 ]
tags = local.common_tags}
設定内容:
- 全ルートを処理するLambda関数
- API Gateway HTTP/REST APIトリガー
- IAMロールと権限
- CloudWatchロググループ
- X-Rayトレーシング設定
- CORS設定
モジュール出力:
output "api_url" { value = module.my_api.stage_invoke_url}
output "lambda_function_name" { value = module.my_api.lambda_function_name}
output "lambda_execution_role_arn" { value = module.my_api.lambda_execution_role_arn}
CORSカスタマイズ例:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
cors_allow_origins = ["https://myapp.com", "https://staging.myapp.com"] cors_allow_methods = ["GET", "POST", "PUT", "DELETE"] cors_allow_headers = [ "authorization", "content-type", "x-custom-header" ]
tags = local.common_tags}
インテグレーション
Section titled “インテグレーション”REST/HTTP API CDKコンストラクトは、各オペレーションの統合を定義するための型安全なインターフェースを提供するように設定されています。
デフォルト統合
Section titled “デフォルト統合”静的メソッドdefaultIntegrations
を使用して、各オペレーションに個別のAWS Lambda関数を定義するデフォルトパターンを利用できます:
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});
Terraformモジュールはデフォルトで単一Lambda関数を使用するルーターパターンを採用します。追加設定は不要です:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# モジュールは自動的にすべてのAPIオペレーションを処理する # 単一のLambda関数を作成します tags = local.common_tags}
統合へのアクセス
Section titled “統合へのアクセス”APIコンストラクトのintegrations
プロパティを通じて、型安全な方法で基盤となるAWS Lambda関数にアクセスできます。例えば、APIがsayHello
というオペレーションを定義している場合、この関数に権限を追加するには次のようにします:
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});
// sayHelloはAPIで定義されたオペレーションに型付けされますapi.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({ effect: Effect.ALLOW, actions: [...], resources: [...],}));
Terraformのルーターパターンでは単一のLambda関数のみが存在します。モジュール出力を通じてアクセスできます:
# 単一Lambda関数に追加権限を付与resource "aws_iam_role_policy" "additional_permissions" { name = "additional-api-permissions" role = module.my_api.lambda_execution_role_name
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "s3:GetObject", "s3:PutObject" ] Resource = "arn:aws:s3:::my-bucket/*" } ] })}
デフォルトオプションのカスタマイズ
Section titled “デフォルトオプションのカスタマイズ”デフォルト統合で作成されるLambda関数のオプションをカスタマイズするには、withDefaultOptions
メソッドを使用します。例えば、すべてのLambda関数をVPC内に配置する場合:
const vpc = new Vpc(this, 'Vpc', ...);
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withDefaultOptions({ vpc, }) .build(),});
VPC設定などのオプションをカスタマイズするには、生成されたTerraformモジュールを編集します。例として、すべてのLambda関数にVPCサポートを追加する場合:
# VPC変数を追加variable "vpc_subnet_ids" { description = "Lambda関数用VPCサブネットIDのリスト" type = list(string) default = []}
variable "vpc_security_group_ids" { description = "Lambda関数用VPCセキュリティグループIDのリスト" type = list(string) default = []}
# Lambda関数リソースを更新resource "aws_lambda_function" "api_lambda" { # ... 既存の設定 ...
# VPC設定を追加 vpc_config { subnet_ids = var.vpc_subnet_ids security_group_ids = var.vpc_security_group_ids }}
VPC設定付きでモジュールを使用:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# VPC設定 vpc_subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id] vpc_security_group_ids = [aws_security_group.lambda_sg.id]
tags = local.common_tags}
統合のオーバーライド
Section titled “統合のオーバーライド”withOverrides
メソッドを使用して特定のオペレーションの統合をオーバーライドできます。各オーバーライドは、HTTPまたはREST APIに適したCDK統合コンストラクトに型付けされたintegration
プロパティを指定する必要があります。例として、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(),});
// 後で別ファイルで、定義したbucketプロパティに// 型安全にアクセスできます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(...) // REST用、HTTP APIの場合はHttpUserPoolAuthorizer } }, }) .build(),});
必要に応じて、デフォルト統合を使用せずに各オペレーションに直接統合を指定できます。例えば、オペレーションごとに異なる統合タイプを使用する場合や、新しいオペレーション追加時に型エラーを受け取りたい場合に有用です:
new MyApi(this, 'MyApi', { integrations: { sayHello: { integration: new LambdaIntegration(...), }, getDocumentation: { integration: new HttpIntegration(...), }, },});
Terraformで明示的なオペレーションごとの統合を行うには、生成されたアプリ固有モジュールを修正してデフォルトのプロキシ統合を置き換えます。
packages/common/terraform/src/app/apis/my-api/my-api.tf
を編集:
- デフォルトのプロキシルートを削除(例:
resource "aws_apigatewayv2_route" "proxy_routes"
) - 単一Lambda関数を個別関数で置き換え
- オペレーションごとに特定の統合とルートを作成(同じZIPバンドルを再利用)
# デフォルトの単一Lambda関数を削除 resource "aws_lambda_function" "api_lambda" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApiHandler" role = aws_iam_role.lambda_execution_role.arn handler = "index.handler" runtime = "nodejs22.x" timeout = 30 # ... その他の設定 }
# デフォルトのプロキシ統合を削除 resource "aws_apigatewayv2_integration" "lambda_integration" { api_id = module.http_api.api_id integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.api_lambda.invoke_arn # ... その他の設定 }
# デフォルトのプロキシルートを削除 resource "aws_apigatewayv2_route" "proxy_routes" { for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]) api_id = module.http_api.api_id route_key = "${each.key} /{proxy+}" target = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}" # ... その他の設定 }
# 同じバンドルを使用してオペレーションごとに個別Lambda関数を追加 resource "aws_lambda_function" "say_hello_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-SayHello" role = aws_iam_role.lambda_execution_role.arn handler = "sayHello.handler" # このオペレーション用の特定ハンドラ runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = merge({ AWS_CONNECTION_REUSE_ENABLED = "1" }, var.env) }
tags = var.tags }
resource "aws_lambda_function" "get_documentation_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-GetDocumentation" role = aws_iam_role.lambda_execution_role.arn handler = "getDocumentation.handler" # このオペレーション用の特定ハンドラ runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = merge({ AWS_CONNECTION_REUSE_ENABLED = "1" }, var.env) }
tags = var.tags }
# オペレーションごとに特定の統合を追加 resource "aws_apigatewayv2_integration" "say_hello_integration" { api_id = module.http_api.api_id integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.say_hello_handler.invoke_arn payload_format_version = "2.0" timeout_milliseconds = 30000 }
resource "aws_apigatewayv2_integration" "get_documentation_integration" { api_id = module.http_api.api_id integration_type = "HTTP_PROXY" integration_uri = "https://example.com/documentation" integration_method = "GET" }
# オペレーションごとに特定のルートを追加 resource "aws_apigatewayv2_route" "say_hello_route" { api_id = module.http_api.api_id route_key = "POST /sayHello" target = "integrations/${aws_apigatewayv2_integration.say_hello_integration.id}" authorization_type = "AWS_IAM" }
resource "aws_apigatewayv2_route" "get_documentation_route" { api_id = module.http_api.api_id route_key = "GET /documentation" target = "integrations/${aws_apigatewayv2_integration.get_documentation_integration.id}" authorization_type = "NONE" }
# 各関数にLambda権限を追加 resource "aws_lambda_permission" "say_hello_permission" { statement_id = "AllowExecutionFromAPIGateway-SayHello" action = "lambda:InvokeFunction" function_name = aws_lambda_function.say_hello_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.http_api.api_execution_arn}/*/*" }
resource "aws_lambda_permission" "get_documentation_permission" { statement_id = "AllowExecutionFromAPIGateway-GetDocumentation" action = "lambda:InvokeFunction" function_name = aws_lambda_function.get_documentation_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.http_api.api_execution_arn}/*/*" }
# デフォルトの単一Lambda関数を削除 resource "aws_lambda_function" "api_lambda" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApiHandler" role = aws_iam_role.lambda_execution_role.arn handler = "index.handler" runtime = "nodejs22.x" timeout = 30 # ... その他の設定 }
# デフォルトのプロキシ統合を削除 resource "aws_apigatewayv2_integration" "lambda_integration" { api_id = module.http_api.api_id integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.api_lambda.invoke_arn # ... その他の設定 }
# デフォルトのプロキシルートを削除 resource "aws_apigatewayv2_route" "proxy_routes" { for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]) api_id = module.http_api.api_id route_key = "${each.key} /{proxy+}" target = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}" # ... その他の設定 }
# 同じバンドルを使用してオペレーションごとに個別Lambda関数を追加 resource "aws_lambda_function" "say_hello_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-SayHello" role = aws_iam_role.lambda_execution_role.arn handler = "sayHello.handler" # このオペレーション用の特定ハンドラ runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = merge({ AWS_CONNECTION_REUSE_ENABLED = "1" }, var.env) }
tags = var.tags }
resource "aws_lambda_function" "get_documentation_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-GetDocumentation" role = aws_iam_role.lambda_execution_role.arn handler = "getDocumentation.handler" # このオペレーション用の特定ハンドラ runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = merge({ AWS_CONNECTION_REUSE_ENABLED = "1" }, var.env) }
tags = var.tags }
# オペレーションごとに特定のリソースとメソッドを追加 resource "aws_api_gateway_resource" "say_hello_resource" { rest_api_id = module.rest_api.api_id parent_id = module.rest_api.api_root_resource_id path_part = "sayHello" }
resource "aws_api_gateway_method" "say_hello_method" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.say_hello_resource.id http_method = "POST" authorization = "AWS_IAM" }
resource "aws_api_gateway_integration" "say_hello_integration" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.say_hello_resource.id http_method = aws_api_gateway_method.say_hello_method.http_method
integration_http_method = "POST" type = "AWS_PROXY" uri = aws_lambda_function.say_hello_handler.invoke_arn }
resource "aws_api_gateway_resource" "get_documentation_resource" { rest_api_id = module.rest_api.api_id parent_id = module.rest_api.api_root_resource_id path_part = "documentation" }
resource "aws_api_gateway_method" "get_documentation_method" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.get_documentation_resource.id http_method = "GET" authorization = "NONE" }
resource "aws_api_gateway_integration" "get_documentation_integration" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.get_documentation_resource.id http_method = aws_api_gateway_method.get_documentation_method.http_method
integration_http_method = "GET" type = "HTTP" uri = "https://example.com/documentation" }
# デプロイメントの依存関係を更新~ resource "aws_api_gateway_deployment" "api_deployment" { rest_api_id = module.rest_api.api_id
depends_on = [ aws_api_gateway_integration.lambda_integration, aws_api_gateway_integration.say_hello_integration, aws_api_gateway_integration.get_documentation_integration, ]
lifecycle { create_before_destroy = true }
triggers = { redeployment = sha1(jsonencode([ aws_api_gateway_integration.say_hello_integration, aws_api_gateway_integration.get_documentation_integration, ])) } }
# 各関数にLambda権限を追加 resource "aws_lambda_permission" "say_hello_permission" { statement_id = "AllowExecutionFromAPIGateway-SayHello" action = "lambda:InvokeFunction" function_name = aws_lambda_function.say_hello_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.rest_api.api_execution_arn}/*/*" }
resource "aws_lambda_permission" "get_documentation_permission" { statement_id = "AllowExecutionFromAPIGateway-GetDocumentation" action = "lambda:InvokeFunction" function_name = aws_lambda_function.get_documentation_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.rest_api.api_execution_arn}/*/*" }
ルーターパターン
Section titled “ルーターパターン”すべてのAPIリクエストを処理する単一のLambda関数をデプロイしたい場合、APIのdefaultIntegrations
メソッドを編集して、統合ごとではなく単一の関数を作成できます:
export class MyApi<...> extends ... {
public static defaultIntegrations = (scope: Construct) => { const router = new Function(scope, 'RouterHandler', { ... }); return IntegrationBuilder.rest({ ... defaultIntegrationOptions: {}, buildDefaultIntegration: (op) => { return { // すべての統合で同じルータLambdaハンドラを参照 integration: new LambdaIntegration(router), }; }, }); };}
必要に応じて、router
関数をdefaultIntegrations
のパラメータとして定義するなど、他の方法でコードを修正することもできます。
Terraformモジュールはデフォルトでルーターパターンを使用します - これはデフォルトかつ唯一のサポートされるアプローチです。生成されたモジュールは、すべてのAPIオペレーションを処理する単一のLambda関数を作成します。
デフォルトモジュールをインスタンス化するだけでルーターパターンを取得できます:
# デフォルトのルーターパターン - 全オペレーション用単一Lambda関数module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# 単一Lambda関数が全オペレーションを自動処理 tags = local.common_tags}
PythonのFastAPI操作とTypeScriptのCDKインフラ間で型安全な連携のため、メタデータ生成を実装
common/constructs
の project.json
に generate:<ApiName>-metadata
ターゲットを追加。ビルド時に packages/common/constructs/src/generated/my-api/metadata.gen.ts
を生成(バージョン管理対象外)
アクセス権付与(IAM認証時)
Section titled “アクセス権付与(IAM認証時)”IAM
認証選択時、grantInvokeAccess
メソッドでAPIアクセスを許可:
api.grantInvokeAccess(myIdentityPool.authenticatedRole);
resource "aws_iam_policy" "api_invoke_policy" { name = "MyApiInvokePolicy" description = "Policy to allow invoking the FastAPI"
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = "execute-api:Invoke" Resource = "${module.my_api.api_execution_arn}/*/*" } ] })}
resource "aws_iam_role_policy_attachment" "api_invoke_access" { role = aws_iam_role.authenticated_user_role.name policy_arn = aws_iam_policy.api_invoke_policy.arn}
resource "aws_iam_role_policy_attachment" "api_invoke_access_existing" { role = "MyExistingRole" policy_arn = aws_iam_policy.api_invoke_policy.arn}
主要出力:
module.my_api.api_execution_arn
(実行権限用)module.my_api.api_arn
module.my_api.lambda_function_arn
ローカル開発
Section titled “ローカル開発”ジェネレータが設定する開発サーバを起動:
pnpm nx run my-api:serve
yarn nx run my-api:serve
npx nx run my-api:serve
bunx nx run my-api:serve
起動時機能:
- コード変更時の自動リロード
/docs
または/redoc
で対話型APIドキュメント/openapi.json
でOpenAPIスキーマ
FastAPIの呼び出し
Section titled “FastAPIの呼び出し”ReactウェブサイトからAPIを呼び出すには api-connection
ジェネレータを使用