AWS Lambdaで作るサーバーサイド

2022.08.28
はじめに
AWS上でAPIを作ろうとしたとき, 気になるのはやはり費用です。
特に趣味の範囲ではリクエストが飛ぶこともそうそうないのでEC2インスタンスを立ち上げっぱなしにするのはもったいない, けれども毎回落とすのは面倒。
そんなときのテンプレート, API Gateway + Lambda + DynamoDBでAPIを作ってみました。
それぞれの役割
Lambda
コードをアップロードすることでイベント発生をトリガーとして任意の処理を実行できます。 今回の場合だとDynamo DBからデータをとってきて、responseとして返す処理を担当します。実行時間分のみ請求されるためEC2インスタンスに比べ安価になるケースが多いです。
API Gateway
APIを作成し、AWSの他のサービスへとつなげる役割を果たします。
今回はGETメソッドのエンドポイントを提供し、HTTPリクエストをトリガーとしてLambdaを呼び出します。
Dynamo DB
key-value形式のNoSQLデータベース。
手順
今回はaccessKeyを指定してユーザーテーブルの指定されたユーザーを返すAPIを作ります。
1. Lambdaの関数を作成
今回はmainという名前で関数を作成します。普通LambdaではNode.jsを使うことが多いですが今回は個人的に最近使っているGoで書きます。
package main
import (
"context"
"lead-growth-api/user"
"log"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
echoadapter "github.com/awslabs/aws-lambda-go-api-proxy/echo"
"github.com/labstack/echo/v4"
)
var echoLambda *echoadapter.EchoLambda
func init() {
// stdout and stderr are sent to AWS CloudWatch Logs
log.Printf("echo cold start")
e := echo.New()
e.GET("/user", func(c echo.Context) error {
return user.GetUser(c)
})
echoLambda = echoadapter.New(e)
}
func handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
return echoLambda.ProxyWithContext(ctx, req)
}
func main() {
lambda.Start(handler)
}
package user
type UserMaster struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"e-mail"`
}
package user
import (
"fmt"
"net/http"
"github.com/labstack/echo/v4"
)
// DBからuserを取得
func GetUser(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"message": "hello"})
}
ポイントはaws-lambda-go-api-proxyのechoを使っている点です。このためにinitにe.GET
等を追加するだけで他のエンドポイントを追加することができます。
あとはmain.goをbuildしてアップロードするのですが、lambdaで動くようにするには以下のように環境変数を設定する必要があります。
build:
GOARCH=amd64 GOOS=linux CGO_ENABLED=0 go build -o main main.go
zip main.zip main
rm main
これで
$ make build
でmain.zipを作りuploadします。
現時点ではDynamoDBと接続しておらず、ただ{message: hello}
を返しているだけです。
2. API Gatewayの設定
以下のように/userにGETメソッドを追加し、以下のようにGETメソッドを作成します。
Lambdaプロキシ統合に使用にチェックを入れるのを忘れないでください。これがないとmainの
e.GET("/user", func(c echo.Context) error {
return user.GetUser(c)
})
が呼ばれません。
API Gatewayのテストで{"message":"hello"}が返るか確かめてください。
3. DynamoDBテーブルの作成
Dynamo DBのテーブルの作成に進み、以下のようなテーブルを作ります。
また、Lambdaで取得するためにレコードを追加しておきます。
4. LambdaとDynamoDBをつなぐ
GetUserを以下のように書き換えます。
func GetUser(c echo.Context, db *dynamodb.DynamoDB) error {
params := &dynamodb.GetItemInput{
TableName: aws.String("users"),
Key: map[string]*dynamodb.AttributeValue{
"id": {
S: aws.String("1"),
},
},
}
result, err := db.GetItem(params)
if err != nil {
return err
}
usr := UserMaster{}
err = dynamodbattribute.UnmarshalMap(result.Item, &usr)
return c.JSON(http.StatusOK, usr)
}
さいごに
今回は省略しましたがDynamoDBへのアクセス権をLambdaに付与する必要があります。
こちらを参考にポリシーを作成し、Lambdaのroleに付与してください。
参考サイト
チュートリアル: Lambda と DynamoDB を使用した CRUD API の構築
API Gateway(HTTP API)のHTTPプロキシ統合とGoを利用したLambda Functionの実装方法
How to Create an AWS IAM Policy to Grant AWS Lambda Access to an Amazon DynamoDB Table