AppSyncのデータソースとしてgolangのLambdaを利用する
特にデロイト トーマツ グループ入りする2021年8月より前の記事については、実態と大きく乖離している場合があります。
ご注意ください。
AWS AppSyncのデータソースとして、
GO言語のLambdaハンドラーを指定した環境を構築したいと思います。
概要図
今回は、以下の図の通りに構築します。
AppSyncはMutationおよび、Queryメソッドを利用して、
Lambda Function経由でDynamoDBにアクセスし、結果を返却します。
AppSync API
最初にAppSyncのAPIを作成します。
今回はカスタムAPIのため、「Build from scratch」を選択します。
DynamoDB
DynamoDBのテーブルを作成します。
今回はString型のpk, skを定義したシンプルなテーブルとします。

AppSync Schema
AppSync APIのSchemaを記述します。
今回はPostの型にfieldという項目を設け、
Lambdaの処理を分岐させるように実装しました。
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
}
Lambda Handler
Lambdaハンドラーを実装します。
今回は、Serverless Application Model(SAM)を用いました。
golangのswitch構文によりfieldの値で処理が分岐し、
DynamoDBへのputまたは、getを実行します。
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("[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("appsync-lambda-go"),
}
_, 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("appsync-lambda-go"),
}
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)
}
AppSync DataSource
実装済みのLambda FunctionをAppSyncのデータソースに指定します。

AppSync Schema Resolver
Mutationおよび、QueryのResolverを指定します。
今回は下記のリクエスト・レスポンスマッピングテンプレートを設定しました。
request mapping template.
{
"version" : "2017-02-28",
"operation": "Invoke",
"payload": $util.toJson($context.arguments)
}
response mapping template.
$util.toJson($context.result)
動作確認
動作確認をしていきます。
Mutation
AppSyncのQueries画面で下記クエリを記述し、実行します。
mutation PutPost {
putPost(field: "putPost", pk: "hoge", sk: "huga", title: "Hello! AppSync!!") {
pk
sk
title
}
}
実行結果


DynamoDBにデータが保存され、レスポンスも期待した値が返却されました!
Query
AppSyncのQueriess画面に下記クエリを記述し、実行します。
query GetPost {
singlePost(field: "singlePost", pk: "hoge", sk: "huga") {
pk
sk
title
}
}
実行結果

Mutationで登録したデータが、取得できています!
データソースにLambdaを指定した場合の実装は無限大
Lambdaで処理を記述することで、その実装は無限大です。
例えば、以下の図のようにS3にリクエストパラメータを保存し、
分析用途としても利用することもできるでしょう。
改善点
今回はリクエストパラメータにfieldという項目を用意し、
Lambdaの処理を分岐しました。
しかしながら、AppSyncのためのAppSyncResolverTemplateを利用することが
最適解ではないかと思います。
AppSyncResolverTemplateを利用するよう、改善していければと思います。
AppSyncResolverTemplateによる実装例
package main
import (
"context"
"fmt"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
func handler(ctx context.Context, event events.AppSyncResolverTemplate) error {
fmt.Printf("Version: %s
", event.Version)
fmt.Printf("Operation: %s
", event.Operation)
fmt.Printf("Payload: %s
", string(event.Payload))
return nil
}
MMMは、会社としてもAWS Lambdaに力を入れています。ぜひ以下のページもあわせてご覧ください。
