Smithy TypeScriptのAPI
Smithyはプロトコルに依存しないインターフェース定義言語で、モデル駆動のアプローチでAPIを設計できます。
Smithy TypeScript APIジェネレータは、サービス定義にSmithyを、実装にSmithy TypeScript Server SDKを使用した新しいAPIを作成します。このジェネレータはCDKまたはTerraformのInfrastructure as Codeを提供し、AWS LambdaにデプロイされたサービスをAWS API Gateway REST API経由で公開します。Smithyモデルからの自動コード生成により、型安全なAPI開発を実現します。生成されたハンドラはAWS Lambda Powertools for TypeScriptを使用し、ロギング、AWS X-Rayトレーシング、CloudWatchメトリクスなどの観測可能性を備えています。
Smithy TypeScript APIの生成
Section titled “Smithy TypeScript APIの生成”新しいSmithy TypeScript APIは2つの方法で生成できます:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)
"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#smithy-api
- 必須パラメータを入力
- クリック
Generate
pnpm nx g @aws/nx-plugin:ts#smithy-api
yarn nx g @aws/nx-plugin:ts#smithy-api
npx nx g @aws/nx-plugin:ts#smithy-api
bunx nx g @aws/nx-plugin:ts#smithy-api
変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:ts#smithy-api --dry-run
yarn nx g @aws/nx-plugin:ts#smithy-api --dry-run
npx nx g @aws/nx-plugin:ts#smithy-api --dry-run
bunx nx g @aws/nx-plugin:ts#smithy-api --dry-run
パラメータ | 型 | デフォルト | 説明 |
---|---|---|---|
name 必須 | string | - | The name of the API (required). Used to generate class names and file paths. |
namespace | string | - | The namespace for the Smithy API. Defaults to your monorepo scope |
computeType | string | ServerlessApiGatewayRestApi | The type of compute to use to deploy this API. |
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 | Inherit | The preferred IaC provider. By default this is inherited from your initial selection. |
ジェネレータの出力
Section titled “ジェネレータの出力”ジェネレータは<directory>/<api-name>
ディレクトリに2つの関連プロジェクトを作成します:
Directorymodel/ Smithyモデルプロジェクト
- project.json プロジェクト設定とビルドターゲット
- smithy-build.json Smithyビルド設定
- build.Dockerfile Smithyアーティファクトビルド用Docker設定
Directorysrc/
- main.smithy メインサービス定義
Directoryoperations/
- echo.smithy オペレーション定義例
Directorybackend/ TypeScriptバックエンド実装
- project.json プロジェクト設定とビルドターゲット
- rolldown.config.ts バンドル設定
Directorysrc/
- handler.ts AWS Lambdaハンドラ
- local-server.ts ローカル開発サーバ
- service.ts サービス実装
- context.ts サービスコンテキスト定義
Directoryoperations/
- echo.ts オペレーション実装例
Directorygenerated/ 生成されたTypeScript SDK(ビルド時に作成)
- …
インフラストラクチャ
Section titled “インフラストラクチャ”このジェネレータは選択したiacProvider
に基づいたInfrastructure as Codeを作成するため、packages/common
にCDKコンストラクトまたはTerraformモジュールを含むプロジェクトを生成します。
共通のInfrastructure as Codeプロジェクトの構造は以下の通りです:
Directorypackages/common/constructs
Directorysrc
Directoryapp/ プロジェクト/ジェネレータ固有のインフラストラクチャ用コンストラクト
Directoryapis/
- <project-name>.ts APIデプロイ用CDKコンストラクト
Directorycore/ 再利用可能な汎用コンストラクト
Directoryapi/
- rest-api.ts REST APIデプロイ用コンストラクト
- utils.ts APIコンストラクト用ユーティリティ
- index.ts コンストラクトエクスポート用エントリポイント
- project.json プロジェクトビルドターゲットと設定
Directorypackages/common/terraform
Directorysrc
Directoryapp/ プロジェクト/ジェネレータ固有のインフラストラクチャ用Terraformモジュール
Directoryapis/
Directory<project-name>/
- <project-name>.tf APIデプロイ用モジュール
Directorycore/ 再利用可能な汎用モジュール
Directoryapi/
Directoryrest-api/
- rest-api.tf REST APIデプロイ用モジュール
- project.json プロジェクトビルドターゲットと設定
Smithy APIの実装
Section titled “Smithy APIの実装”Smithyでのオペレーション定義
Section titled “Smithyでのオペレーション定義”オペレーションはモデルプロジェクト内のSmithyファイルで定義されます。メインサービス定義はmain.smithy
にあります:
$version: "2.0"
namespace your.namespace
use aws.protocols#restJson1use smithy.framework#ValidationException
@title("YourService")@restJson1service YourService { version: "1.0.0" operations: [ Echo, // オペレーションを追加 ] errors: [ ValidationException ]}
個々のオペレーションはoperations/
ディレクトリの別ファイルで定義します:
$version: "2.0"
namespace your.namespace
@http(method: "POST", uri: "/echo")operation Echo { input: EchoInput output: EchoOutput}
structure EchoInput { @required message: String
foo: Integer bar: String}
structure EchoOutput { @required message: String}
TypeScriptでのオペレーション実装
Section titled “TypeScriptでのオペレーション実装”オペレーション実装はバックエンドプロジェクトのsrc/operations/
ディレクトリに配置されます。各オペレーションはSmithyモデルからビルド時に生成されるTypeScript Server SDKの型を使用して実装されます。
import { ServiceContext } from '../context.js';import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input) => { // ビジネスロジックを実装 return { message: `Echo: ${input.message}` // Smithyモデルに基づく型安全 };};
オペレーションはsrc/service.ts
でサービス定義に登録する必要があります:
import { ServiceContext } from './context.js';import { YourServiceService } from './generated/ssdk/index.js';import { Echo } from './operations/echo.js';// 他のオペレーションをインポート
// サービスにオペレーションを登録export const Service: YourServiceService<ServiceContext> = { Echo, // 他のオペレーションを追加};
サービスコンテキスト
Section titled “サービスコンテキスト”context.ts
でオペレーション間で共有するコンテキストを定義できます:
export interface ServiceContext { // PowertoolsのTracer、Logger、Metricsがデフォルトで提供 tracer: Tracer; logger: Logger; metrics: Metrics; // 共有依存関係、データベース接続などを追加 dbClient: any; userIdentity: string;}
このコンテキストはすべてのオペレーション実装に渡され、データベース接続や設定、ロギングユーティリティなどのリソース共有に使用できます。
AWS Lambda Powertoolsによる観測可能性
Section titled “AWS Lambda Powertoolsによる観測可能性”構造化ロギングはMiddyミドルウェア経由で自動コンテキスト注入されるAWS Lambda Powertoolsで設定されます。
export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>() .use(captureLambdaHandler(tracer)) .use(injectLambdaContext(logger)) .use(logMetrics(metrics)) .handler(lambdaHandler);
コンテキストからロガーを参照できます:
import { ServiceContext } from '../context.js';import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => { ctx.logger.info('ログメッセージ'); // ...};
トレーシング
Section titled “トレーシング”AWS X-RayトレーシングはcaptureLambdaHandler
ミドルウェアで自動設定されます。
export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>() .use(captureLambdaHandler(tracer)) .use(injectLambdaContext(logger)) .use(logMetrics(metrics)) .handler(lambdaHandler);
オペレーション内でカスタムサブセグメントを追加できます:
import { ServiceContext } from '../context.js';import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => { // 新しいサブセグメントを作成 const subsegment = ctx.tracer.getSegment()?.addNewSubsegment('custom-operation'); try { // ロジックを実装 } catch (error) { subsegment?.addError(error as Error); throw error; } finally { subsegment?.close(); }};
CloudWatchメトリクスはlogMetrics
ミドルウェアで自動収集されます。
export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>() .use(captureLambdaHandler(tracer)) .use(injectLambdaContext(logger)) .use(logMetrics(metrics)) .handler(lambdaHandler);
オペレーション内でカスタムメトリクスを追加できます:
import { MetricUnit } from '@aws-lambda-powertools/metrics';import { ServiceContext } from '../context.js';import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => { ctx.metrics.addMetric("CustomMetric", MetricUnit.Count, 1); // ...};
エラーハンドリング
Section titled “エラーハンドリング”Smithyは組み込みのエラーハンドリングを提供します。カスタムエラーはSmithyモデルで定義できます:
@error("client")@httpError(400)structure InvalidRequestError { @required message: String}
オペレーション/サービスに登録:
operation MyOperation { ... errors: [InvalidRequestError]}
TypeScript実装でスロー:
import { InvalidRequestError } from '../generated/ssdk/index.js';
export const MyOperation: MyOperationHandler<ServiceContext> = async (input) => { if (!input.requiredField) { throw new InvalidRequestError({ message: "必須フィールドが不足しています" }); }
return { /* 成功レスポンス */ };};
ビルドとコード生成
Section titled “ビルドとコード生成”SmithyモデルプロジェクトはDockerを使用してSmithyアーティファクトをビルドし、TypeScript Server SDKを生成します:
pnpm nx run <model-project>:build
yarn nx run <model-project>:build
npx nx run <model-project>:build
bunx nx run <model-project>:build
このプロセスでは:
- Smithyモデルのコンパイルと検証
- SmithyモデルからOpenAPI仕様の生成
- 型安全なオペレーションインターフェースを含むTypeScript Server SDKの作成
- ビルドアーティファクトを
dist/<model-project>/build/
に出力
バックエンドプロジェクトはビルド時に生成されたSDKを自動コピーします:
pnpm nx run <backend-project>:copy-ssdk
yarn nx run <backend-project>:copy-ssdk
npx nx run <backend-project>:copy-ssdk
bunx nx run <backend-project>:copy-ssdk
バンドルターゲット
Section titled “バンドルターゲット”ジェネレーターは自動的に Rolldown を使用する bundle
ターゲットを設定します。このターゲットはデプロイメントパッケージの作成に使用されます:
pnpm nx run <project-name>:bundle
yarn nx run <project-name>:bundle
npx nx run <project-name>:bundle
bunx nx run <project-name>:bundle
Rolldownの設定はrolldown.config.ts
に記述され、生成するバンドルごとにエントリを定義します。Rolldownは定義された複数のバンドルを並行して作成する処理を管理します。
ローカル開発
Section titled “ローカル開発”ホットリロード機能を備えたローカル開発サーバが設定されます:
pnpm nx run <backend-project>:serve
yarn nx run <backend-project>:serve
npx nx run <backend-project>:serve
bunx nx run <backend-project>:serve
Smithy APIのデプロイ
Section titled “Smithy APIのデプロイ”ジェネレータは選択したiacProvider
に基づいてCDKまたはTerraformインフラストラクチャを作成します。
APIデプロイ用CDKコンストラクトはcommon/constructs
フォルダにあります:
import { MyApi } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { // APIをスタックに追加 const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(), }); }}
これにより以下が設定されます:
- Smithyサービス用AWS Lambda関数
- API Gateway REST APIトリガー
- IAMロールと権限
- CloudWatchロググループ
- X-Rayトレーシング設定
APIデプロイ用Terraformモジュールはcommon/terraform
フォルダにあります:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# Lambda関数用環境変数 env = { ENVIRONMENT = var.environment LOG_LEVEL = "INFO" }
# 追加IAMポリシー additional_iam_policy_statements = [ # APIに必要な追加権限 ]
tags = local.common_tags}
これにより以下が設定されます:
- Smithy APIを提供するAWS Lambda関数
- API Gateway REST APIトリガー
- IAMロールと権限
- CloudWatchロググループ
- X-Rayトレーシング設定
- CORS設定
Terraformモジュールは複数の出力を提供します:
# APIエンドポイントにアクセスoutput "api_url" { value = module.my_api.stage_invoke_url}
# Lambda関数詳細にアクセスoutput "lambda_function_name" { value = module.my_api.lambda_function_name}
インテグレーション
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}
Smithyで定義されたオペレーションのメタデータをCDKコンストラクトに提供するため、コード生成を使用します。
共通コンストラクトの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);
# API呼び出しを許可するIAMポリシーresource "aws_iam_policy" "api_invoke_policy" { name = "MyApiInvokePolicy" description = "Smithy API呼び出し許可ポリシー"
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = "execute-api:Invoke" Resource = "${module.my_api.api_execution_arn}/*/*" } ] })}
# IAMロールにポリシーをアタッチ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}
Smithy APIの呼び出し
Section titled “Smithy APIの呼び出し”ReactウェブサイトからAPIを呼び出すには、api-connection
ジェネレータを使用できます。これはSmithyモデルから型安全なクライアントを生成します。