こんにちは。
SARAH FoodDataBankの沈(シム)です。
弊社のFoodDataBankはAWSのAppsync + Lambdaの組み合わせてサービスを提供しているので、勉強のため、改めてAppsync + Lambdaの組み合わせを実施してみました。
役に立つ対象
既存のREST APIまたはAPI Gatewayを用いてAPIを実装していた方
AppsyncでAPIを実装したい方
システム図
必要なAWSリソース
- Appsync
- Lambda
- DynamoDB
- IAM Role(Lambda用、AppSync用)
- SAM
AppSync作成
まず、AppSyncを作成します。
AWS Consoleで「API 作成」ボタンを押下し、「一から構築」を選択し、「開始」ボタンをクリック
AppSync APIが作成できると、左メニューの「スキーマ」をクリックし、スキーマの中身を以下のように設定します。
その後、「スキーマ保存」ボタンをクリック
スキーマ
type Mutation {
putPost(
field: String!,
pk: String!,
sk: String!,
title: String!
): Post
}
type Post {
field: String!
pk: String!
sk: String!
title: String
}
type Query {
singlePost(field: String!, pk: String!, sk: String!): Post
}
schema {
query: Query
mutation: Mutation
}
DynamoDB作成
AWS consoleにてDynamoDBへ移動し、テーブル作成ボタンをクリックします。
練習用のため、簡単に作ります。
テーブル名は適切な名で入力し、パーティションキーはpk、ソートキーはsk入力し、「テーブル作成」ボタンをクリック
Lambda
// src/main.go
package main
import (
"fmt"
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
)
type Post struct {
Field string `json:"field"`
Pk string `json:"pk"`
Sk string `json:"sk"`
Title string `json:"title"`
}
type Response struct {
Pk string `json:"pk"`
Sk string `json:"sk"`
Title string `json:"title"`
}
func hander(arg Post) (Response, error) {
response := Response{}
fmt.Println("arg: ", arg)
fmt.Println("[DEBUG]Start create session.")
session, err := session.NewSession(
&aws.Config{Region: aws.String("ap-northeast-1")},
)
if err != nil {
return Response{}, err
}
svc := dynamodb.New(session)
switch arg.Field {
case "putPost":
input := &dynamodb.PutItemInput{
Item: map[string]*dynamodb.AttributeValue{
"pk": {
S: aws.String(arg.Pk),
},
"sk": {
S: aws.String(arg.Sk),
},
"title": {
S: aws.String(arg.Title),
},
},
TableName: aws.String("fdb-sim-test-dynamodb"),
}
_, err = svc.PutItem(input)
if err != nil {
return Response{}, err
}
response.Pk = arg.Pk
response.Sk = arg.Sk
response.Title = arg.Title
case "singlePost":
input := &dynamodb.GetItemInput{
Key: map[string]*dynamodb.AttributeValue{
"pk": {
S: aws.String(arg.Pk),
},
"sk": {
S: aws.String(arg.Sk),
},
},
TableName: aws.String("fdb-sim-test-dynamodb"),
}
result, err := svc.GetItem(input)
if err != nil {
return Response{}, err
}
resultData := &Response{}
if err := dynamodbattribute.UnmarshalMap(result.Item, resultData); err != nil {
return Response{}, err
}
response.Pk = resultData.Pk
response.Sk = resultData.Sk
response.Title = resultData.Title
}
return response, nil
}
func main() {
lambda.Start(hander)
}
// src/go.mod
module example.com/main
go 1.18
require (
github.com/aws/aws-lambda-go v1.36.0 // indirect
github.com/aws/aws-sdk-go v1.44.154 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
)
## samconfig.toml
version = 0.1
### Sandbox ###
[default]
[default.deploy]
[default.deploy.parameters]
stack_name = "fdb-sim-test-lambda-go" ## 自分のもので変更してください
s3_prefix = "fdb-sim-test-lambda-go" ## 自分のもので変更してください
region = "ap-northeast-1"
confirm_changeset = false
capabilities = "CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND"
# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Appsync Lambda test
Resources:
TestFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: "fdb-sim-test-lambda"
CodeUri: ./src/
Handler: main
Runtime: go1.x
Timeout: 30
MemorySize: 128
Policies:
- CloudWatchFullAccess
- AmazonDynamoDBFullAccess
TestFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/lambda/fdb-sim-test-lambda"
RetentionInDays: 30
注意点
main.goの中に記入されているDynamoDBの名前は自分のもので変更してください!
TableName: aws.String("fdb-sim-test-dynamodb"),
aws cliで以下のコマンドを入力します。
sam build
sam deploy --resolve-s3
Cloudfrontの新しいスタックが生成され、LambdaとCloudwatch Log、IAMロールが作成されます。
AppSync用IAMロール作成
AWS consoleにてIAMに移動し、ロール作成をクリックします。
「AWSのサービス」を選択し、「他のAWSのサービスのユースケース」でAppSyncを検索して選択します。
ロール名を適切なもので設定してください。
「Service」がappsync.amazonaws.comで設定されているのか確認後、作成ボタンを押下
新しく作成できたロールにLambdaのポリシーを追加します。
※簡単にするため、Lambda_Fullaccessを追加しました。プロダクトには適切な権限を持ったポリシーを追加してください。
AppSync設定
AppSyncに戻り、データセットとリゾルバーを設定します。
左メニューで「データソース」を選択し、新しいデータソースを追加します。
「データソース」名は適切な名を入力し、「データソースタイプ」を「AWS Lambda 関数」で選択します。
リージョンは作成したLambdaのリージョンを選択し、「関数のARN」にて先ほど作成したLambdaのARNを選択します。
ロール選択する時も「既存の役割」を選択して先ほど作成したAppSync用のロールを選択します。
左メニューで「スキーマ」を選択し、右側の「Mutaion」下にあるputPostの「アタッチ」ボタンをクリックします。
「リゾルバータイプ」を「Unit Resolver(VTL only)」を選択し、Saveボタン押下
「データソース名」で先ほど設定したデータソースを選択し、「マッピングテンプレートの設定」でrequest, responseのマッピングテンプレートをEnableにします。
各テンプレートは既にサンプルで提供されているものだけで良いので、
リクエストマッピングはInvoke and forward arguments
レスポンスマッピングはReturn Lambda resultで選択し、「リゾルバー保存」ボタンを押下
テスト
AppSyncの左メニューで「クエリ」をクリックして実際にクエリを投げて疎通テストを行います。
クエリの入力欄に以下のクエリを入れて実行ボタンを押します。
mutation PutPost {
putPost(field: "putPost", pk: "hoge", sk: "huga", title: "Hello! AppSync!!") {
pk
sk
title
}
}
実際にDynamoDBにデータが入っているのか確認してみましょう。
データはちゃんと入ってますね。
まとめ
最近GraphQLでAPIを実装するのが当然ではあり、サーバーレス環境では結構使われているものなので、復習するために改めてAppSync + Lambdaで構築してみました。大体GraphQLのAPIがこんな感じで作られているのを認識できればと思います。
何か不明なものがありましたらコメントつけてください。
今回はAppSyncでリゾルバーを設定したやり方になってますが、AWS側ではダイレクトリゾルバーがあるので、次回はダイレクトリゾルバーを使ってAppSync + Lambdaをやってみようかと思います。
参照サイト
- スキーマやLambdaのコードはこちらのサイトから参照
- go.mod作成する方法
SARAH Tech Blog