さて、第一回と第二回でLambdaとDynamoDBのスタックをそれぞれ作成しデプロイまでやりました。
ただ、このままではアクセスポイントがないため若干不便なので、APIgatewayを準備したいと思います。
また、現状ではスタックを別々に分けていますが、アプリケーション自体が大きくないので、扱いやすくなるためいっそのこと1つのスタックに統合しようと思います。
記述内容自体はほとんど変わりませんが、関数名をNewAppStackと変更しその中にNewDynamoDBStack関数とNewLambdaStack関数をまとめました。これで重複コードも削除できるのですっきりしました。
package main
import (
"log"
"os"
"github.com/aws/aws-cdk-go/awscdk/v2"
"github.com/aws/aws-cdk-go/awscdk/v2/awslambda"
"github.com/aws/aws-cdk-go/awscdk/v2/awsapigatewayv2"
"github.com/aws/aws-cdk-go/awscdk/v2/awsapigatewayv2integrations"
"github.com/aws/constructs-go/constructs/v10"
"github.com/aws/jsii-runtime-go"
"github.com/joho/godotenv"
"github.com/aws/aws-cdk-go/awscdk/v2/awsdynamodb"
)
type AppStackProps struct {
awscdk.StackProps
}
func NewAppStack(scope constructs.Construct, id string, props *AppStackProps) awscdk.Stack {
var sprops awscdk.StackProps
if props != nil {
sprops = props.StackProps
}
stack := awscdk.NewStack(scope, &id, &sprops)
// DynamoDBテーブルの作成
table := awsdynamodb.NewTable(stack, jsii.String("MyDynamoDB"), &awsdynamodb.TableProps{
TableName: jsii.String("MyDynamoDB"),
PartitionKey: &awsdynamodb.Attribute{
Name: jsii.String("id"),
Type: awsdynamodb.AttributeType_STRING,
},
BillingMode: awsdynamodb.BillingMode_PAY_PER_REQUEST,
})
// Lambda関数の定義
lambdaFunction := awslambda.NewFunction(stack, jsii.String("MyFunction"), &awslambda.FunctionProps{
Runtime: awslambda.Runtime_PROVIDED_AL2(),
Handler: jsii.String("main"),
Code: awslambda.Code_FromAsset(jsii.String("lambda/dynamoDBHandler"), nil),
Environment: &map[string]*string{
"DYNAMODB_TABLE_NAME": table.TableName(),
},
})
// テーブルへの権限付与
table.GrantReadWriteData(lambdaFunction)
// API Gatewayの定義
api := awsapigatewayv2.NewHttpApi(stack, jsii.String("MyApi"), &awsapigatewayv2.HttpApiProps{
ApiName: jsii.String("MyApi"),
})
// Lambda関数の統合
lambdaIntegration := awsapigatewayv2integrations.NewHttpLambdaIntegration(jsii.String("MyIntegration"), lambdaFunction, nil)
// API Gatewayの定義
api.AddRoutes(&awsapigatewayv2.AddRoutesOptions{
Path: jsii.String("/test"),
Methods: &[]awsapigatewayv2.HttpMethod{
awsapigatewayv2.HttpMethod_GET,
awsapigatewayv2.HttpMethod_POST,
awsapigatewayv2.HttpMethod_PUT,
awsapigatewayv2.HttpMethod_DELETE,
},
Integration: lambdaIntegration,
})
return stack
}
func main() {
// .envファイルを読み込む
err := godotenv.Load()
if err != nil {
log.Fatalf("Error loading .env file")
}
// スタックの入れ物を作るイメージ
app := awscdk.NewApp(nil)
NewAppStack(app, "AppStack", &AppStackProps{
awscdk.StackProps{
Env: env(),
},
})
app.Synth(nil)
}
func env() *awscdk.Environment {
awsAccountId := os.Getenv("AWS_ACCOUNT_ID")
awsRegion := os.Getenv("AWS_REGION")
return &awscdk.Environment{
Account: jsii.String(awsAccountId),
Region: jsii.String(awsRegion),
}
}
今回、追加した内容としては以下の2点です。
- lambda関数に作成するテーブルへの読み書き操作権限を与えること
- apigatewayを作成しパスを設定し、lambdaと統合すること
テーブルへの権限付与
テーブルへの権限付与については以下の箇所です。GrantWriteData
やGrantReadData
など他にも様々な権限を与えることができます。
今回は作成したlambda関数にテーブルの全ての読み書き操作を許可します。
table.GrantReadWriteData(lambdaFunction)
メソッド名 | 説明 |
---|---|
GrantFullAccess | DynamoDB テーブルに対するすべての操作を許可します。 |
GrantReadData | BatchGetItem、GetRecords、GetShardIterator、Query、GetItem、Scan、DescribeTable など、すべての読み取り操作を許可します。 |
GrantReadWriteData | BatchGetItem、GetRecords、GetShardIterator、Query、GetItem、Scan、BatchWriteItem、PutItem、UpdateItem、DeleteItem、DescribeTable など、すべての読み書き操作を許可します。 |
GrantWriteData | BatchWriteItem、PutItem、UpdateItem、DeleteItem、DescribeTable など、すべての書き込み操作を許可します。 |
GrantStreamRead | DescribeStream、GetRecords、GetShardIterator、ListStreams など、テーブルのストリームに対するすべての読み取り操作を許可します。 |
GrantStream | テーブルのストリームに関連する指定されたアクションを許可します。 |
GrantTableListStreams | 現在の DynamoDB テーブルに接続されているストリームの一覧を IAM プリンシパルに許可します。 |
Grant | 特定のアクションのセットを IAM プリンシパルに対して許可します。 |
GrantCrud | テーブルに対する CRUD(作成、読み取り、更新、削除)操作を許可します。 |
GrantManagement | テーブルの管理操作(例: DescribeTable や ListTables)を許可します。 |
APIgateway
続いてapigatewayを追加します。
以下をimportに追加して、go mod tidy
を実行しましょう。
"github.com/aws/aws-cdk-go/awscdk/v2/awsapigateway"
次にapiを定義して、そのapiとlambda関数を統合しましょう。
api := awsapigatewayv2.NewHttpApi(stack, jsii.String("MyApi"), &awsapigatewayv2.HttpApiProps{})
この関数は、新しいHttpApiオブジェクト(API Gateway HTTP API)を作成するためのコンストラクタです。
まず、awaspigatewayv2.NewHttpApiの定義はscope
とid
と*HttpApiProps
構造体を渡します。
scopeとidはこれまでに出てきたものと同じ概念なので割愛しますが、第三引数にはlambdaやdynamoと同様に設定値を渡すことができます。今回は試しにApiNameを渡してみます。
フィールド名 | 型 | 説明 | 必須 | 具体的な値 |
---|---|---|---|---|
ApiName | *string | APIの名前を指定します。指定しない場合は自動生成されます。 | "MyCustomApi" |
|
CorsPreflight | *CorsPreflightOptions | CORS (Cross-Origin Resource Sharing) 設定を指定します。 | &awsapigatewayv2.CorsPreflightOptions{ AllowOrigins: jsii.Strings("*"), AllowMethods: jsii.Strings("GET", "POST") } |
|
CreateDefaultStage | *bool | デフォルトのステージを作成するかどうかを指定します(デフォルトはtrue)。 | jsii.Bool(true) |
|
DefaultDomainMapping | *DomainMappingOptions | デフォルトのドメインマッピングを設定します。 | &awsapigatewayv2.DomainMappingOptions{ DomainName: domainName } |
|
DefaultIntegration | IHttpRouteIntegration | デフォルトの統合を設定します。 | lambdaIntegration |
|
Description | *string | APIの説明を設定します。 | "This is my custom API" |
|
DisableExecuteApiEndpoint | *bool |
execute-api エンドポイントを無効にするかどうかを指定します。 |
jsii.Bool(false) |
|
FailOnWarnings | *bool | 警告がある場合にデプロイを失敗させるかどうかを指定します。 | jsii.Bool(true) |
これらの設定項目を使って、API Gateway HTTP APIの動作を細かく制御できます。具体的な値の例も含めているので、必要に応じてこれらを参考に設定してください。
Lambda関数の統合
次にAPI Gatewayのルートが呼び出されたときに、指定されたLambda関数が実行されるように設定します。
lambdaIntegration := awsapigatewayv2integrations.NewHttpLambdaIntegration(jsii.String("MyIntegration"), lambdaFunction, &awsapigatewayv2integrations.HttpLambdaIntegrationProps{})
awsapigatewayv2integrations.NewHttpLambdaIntegration
の引数には以下の関数の定義から分かる通り、一意の名前とawslambda.NewFunction
で作成したlambda関数、そして最後にHttpLambdaIntegrationProps
で設定値を渡します。
func NewHttpLambdaIntegration(id *string, handler awslambda.IFunction, props *HttpLambdaIntegrationProps) HttpLambdaIntegration {
パッケージは以下のawsapigatewayv2integrations
をimportして使用します。こちらもgo mod tidy
しましょう。
"github.com/aws/aws-cdk-go/awscdk/v2/awsapigatewayv2integrations"
HttpLambdaIntegrationProps
はHttpLambdaIntegrationProps.go
で定義されていますが、ParameterMapping
とPayloadFormatVersion
だけのようです。
フィールド名 | 型 | 説明 | 必須 | 具体的な値 |
---|---|---|---|---|
ParameterMapping | ParameterMapping | API GatewayのリクエストパラメータをLambda関数に渡す際のマッピングを指定します。 | awsapigatewayv2.ParameterMapping{QueryStrings: map[string]string{"originalParamName": "newParamName"}} |
|
PayloadFormatVersion | PayloadFormatVersion | API GatewayがLambda関数に渡すペイロードのフォーマットバージョンを指定します。 | awsapigatewayv2.PayloadFormatVersion_VERSION_2_0 |
今回は特に必要ないですが、以下のようにマッピングの設定などをしておくと、例えば外部パラメータ名userIdが内部パラメータ名 user_idに変換されるみたいです。ヘッダーも同様。
lambdaIntegration := awsapigatewayv2integrations.NewHttpLambdaIntegration(jsii.String("MyIntegration"), lambdaFunction, &awsapigatewayv2integrations.HttpLambdaIntegrationProps{
ParameterMapping: awsapigatewayv2.ParameterMapping{
QueryStrings: map[string]string{
"userId": jsii.String("user_id"),
},
Headers: map[string]string{
"externalHeader": "internalHeader",
},
},
PayloadFormatVersion: awsapigatewayv2.PayloadFormatVersion_VERSION_2_0,
})
まあ今回は設定しないのでnilとしておきます。とりあえずここまででapiGatewayとそれにlambda関数を統合するための定義ができました。
残すところはapi.AddRoutes(&awsapigatewayv2.AddRoutesOptions
の箇所ですね。
api.AddRoutes(&awsapigatewayv2.AddRoutesOptions{
Path: jsii.String("/test"),
Methods: &[]awsapigatewayv2.HttpMethod{
awsapigatewayv2.HttpMethod_GET,
awsapigatewayv2.HttpMethod_POST,
awsapigatewayv2.HttpMethod_PUT,
awsapigatewayv2.HttpMethod_DELETE,
},
Integration: lambdaIntegration,
})
ここでは、先ほどawsapigatewayv2.NewHttpApi
で定義したapiに設定を追加していきます。
今回はAddRoutes
を使用します。この関数はAddRoutesOptions
を引数にとります。
AddRoutes(options *AddRoutesOptions) *[]HttpRoute
さらにAddRoutesOptions
を見てみるとAddRoutesOptions.go
に以下のように定義されています。
フィールド名 | 型 | 説明 | 必須 | デフォルト |
---|---|---|---|---|
Integration | HttpRouteIntegration | このルートで構成される統合を指定します。 | ⭕️ | |
Path | *string | これらのルートが構成されるパスを指定します。 | ⭕️ | |
AuthorizationScopes | *[]*string | 認証に含めるOIDCスコープのリスト。これらのスコープは、ゲートウェイのデフォルトの認証スコープを上書きします。 | APIで構成されている場合はデフォルトの認証スコープを使用、そうでない場合はなし | |
Authorizer | IHttpRouteAuthorizer | これらのルートに関連付けられるオーソライザー。 | デフォルトのオーソライザーを使用 | |
Methods | *[]HttpMethod | 構成するHTTPメソッドのリスト。 | HttpMethod.ANY |
今回は必須であるPath
とMethods
に加えて、先ほど定義したlambdaIntegrationをIntegration
に設定します。
具体的には、/testパスへのGET、POST、PUT、DELETEリクエストを指定されたLambda関数にルーティングします。この設定により、指定したパスにリクエストが来た場合、対応するLambda関数が実行されます。
さて、これで完成したのでcdk deploy
を実行してみましょう。
すると無事、✅ AppStack✨ Deployment time: 62.46s
と表示されデプロイに成功しました。
AWSのUIで確認してみるときちんとLambdaにAPI Gatewayが統合されてトリガーとして設定されていることが分かります。
DynamoDBについても以前と同様に正常に作成されています。
これで/test
パスへAPIを投げるとトリガーになり、lambda関数に飛ばすことができるようになりました🙌
したがって次はこのDynamoDBに読み書き権限を与えたlambda関数の中でmethodによる条件分岐でテスト処理を書いて、CRUD処理をできるように実装してみたいと思います!