CDK で API Gateway + Lambda (Go)

API Gateway + Lambda (Go) の構成を CDK でデプロイする。

作るもの

{"name":"test"}

というリクエストボディを POST すると

Hello test!

というレスポンスが返ってくる API を作る。

ディレクトリ構成

少し複雑なソースコードを想定したかったので、Go のソースコードは main.go と usecase.go に分割してみた。

test-lambda-go
|
|-cdk
| |-(このディレクトリ内で cdk init する)
|
|-lambda
  |-go.mod
  |-go.sum
  |-cmd
  | |-hello
  |   |-main.go
  |-internal
    |-handler
      |-handler.go

Go のソースコード

go mod init gitlab.com/k1350/test-lambda-go で初期化したとする。

lambda/cmd/hello/main.go

package main

import (
	"github.com/aws/aws-lambda-go/lambda"

	"gitlab.com/k1350/test-lambda-go/internal/handler"
)

func main() {
	lambda.Start(handler.HandleRequest)
}

lambda/internal/handler/handler.go

package handler

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/aws/aws-lambda-go/events"
)

type Input struct {
	Name string `json:"name"`
}

func HandleRequest(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	input, err := parse(request.Body)
	if err != nil {
		return events.APIGatewayProxyResponse{
			Body:       err.Error(),
			StatusCode: 500,
		}, err
	}
	return events.APIGatewayProxyResponse{
		StatusCode: 200,
		Body:       fmt.Sprintf("Hello %s!", input.Name),
	}, nil
}

func parse(inputs string) (*Input, error) {
	var req Input
	err := json.Unmarshal([]byte(inputs), &req)
	if err != nil {
		return nil, err
	}
	return &req, nil
}

少なくとも Body と StatusCode を持つ構造体で返さないと API Gateway 側でレスポンスをパースできずにエラーになる。

CDK のコード

cdk init --language typescript で初期化したとする。

cdk/package.json

cdk init で入った aws-cdk のバージョンと、Go のために入れなければならない @aws-cdk/aws-lambda-go-alpha の依存バージョンが合わないエラーが出たので最終的な package.json を記載する。

{
  "name": "cdk",
  "version": "0.1.0",
  "bin": {
    "cdk": "bin/cdk.js"
  },
  "scripts": {
    "build": "tsc",
    "watch": "tsc -w",
    "test": "jest",
    "cdk": "cdk"
  },
  "devDependencies": {
    "@types/jest": "^29.2.2",
    "@types/node": "18.11.9",
    "@types/prettier": "2.7.1",
    "aws-cdk": "2.50.0",
    "jest": "^29.2.2",
    "ts-jest": "^29.0.3",
    "ts-node": "^10.9.1",
    "typescript": "~4.8.4"
  },
  "dependencies": {
    "@aws-cdk/aws-lambda-go-alpha": "^2.50.0-alpha.0",
    "aws-cdk-lib": "2.50.0",
    "constructs": "^10.0.0",
    "source-map-support": "^0.5.21"
  }
}

cdk/lib/cdk-stack.ts

import { Stack, StackProps, aws_apigateway } from "aws-cdk-lib";
import { Construct } from "constructs";
import { GoFunction } from "@aws-cdk/aws-lambda-go-alpha";

export class CdkStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const lambda = new GoFunction(this, "handler", {
      entry: "../lambda/cmd/hello",
    });

    const api = new aws_apigateway.RestApi(this, "sampleApiGateway");

    const sample = api.root.addResource("sample");
    const integration = new aws_apigateway.LambdaIntegration(lambda);
    sample.addMethod("POST", integration);
  }
}

デプロイと後片付け

cdk deploy すると API が作成される。

cdk destroy で諸々削除されるが、Lambda に zip アップロードするために S3 にファイルを一時置きしており、そのファイルは削除されなかった。
自動で削除する方法は調べたが今のところ不明。とりあえずデプロイ終わったら自分で削除する。

以上