AppSyncのデータソースとしてgolangのLambdaを利用する
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に力を入れています。ぜひ以下のページもあわせてご覧ください。