今から始めるServerless Frameworkでサーバーレスデビュー【マッピングテンプレート番外編#5】
番外編としてマッピングテンプレート編になります。
マッピングテンプレートはServerless Frameworkとは直接的な関係はありませんが、AppSyncに関連するものです。
マッピングテンプレートはVTL(Velocity Template Language)と呼ばれる言語で記述されます。
このデータソースには様々なものが存在します。
例えば、DynamoDBやLambda、Elasticsearchなどがあります。
AppSyncは各データソースとリクエストとレスポンスを介して処理を行うために必要不可欠な存在です。
GraphQLを使用してデータベースへの書き込みや削除などのCRUD操作を行う際に、「特定の条件下では特定の方法でデータを保存する」といった動作を設定するためにマッピングテンプレートが使用されます。
また、「データが書き込まれた日時を自動的にデータベースに保存する」といった処理も行うことができます。
今回はGraphQLの動作を確認しながら、Schema.graphqlと組み合わせてマッピングテンプレートを作成していきます。
// Schema.graphql
# ------------------------------
# Model
# ------------------------------
type Post @model {
postId: ID!
content: String!
userId: ID!
}
type User @model {
userId: ID!
username: String!
email: String!
}
# ------------------------------
# Schema
# ------------------------------
type Mutation {
createPost(input: CreatePostInput!): Post
deletePost(input: DeletePostInput!, expectedVersion: Int): Post
updatePost(input: UpdatePostInput!): Post
}
type Query {
getUser(userId: ID!): User
getPost(postId: ID!): Post
listPost: [Post!]!
}
schema {
query: Query
mutation: Mutation
}
# ------------------------------
# Input
# ------------------------------
input CreatePostInput {
postId: ID
content: String!
userId: ID!
createdAt: AWSDateTime
updatedAt: AWSDateTime
}
input DeletePostInput {
postId: ID!
}
input UpdatePostInput {
postId: ID!
content: String!
userId: ID!
}
Schemaの詳細な書き方については割愛しますが、ここでは投稿(Post)のモデルを持った簡単なクエリとミューテーションを用意しました。
このSchemaに基づいて、以下のVTL(Velocity Template Language)ファイルを用意します。
├── mapping-templates
│ ├── Mutation.createPost.request.vtl
│ ├── Mutation.createPost.response.vtl
│ ├── Mutation.deletePost.request.vtl
│ ├── Mutation.deletePost.response.vtl
│ ├── Mutation.updatePost.request.vtl
│ ├── Mutation.updatePost.response.vtl
│ ├── Query.getPost.request.vtl
│ ├── Query.getPost.response.vtl
│ ├── Query.getUser.request.vtl
│ ├── Query.getUser.response.vtl
│ ├── Query.listPost.request.vtl
│ └── Query.listPost.response.vtl
マッピングテンプレートには必ずリクエストとレスポンスが存在するため、それぞれのファイルをセットで用意します。
1.Query.getUser
// Query.getUser.request.vtl
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"userId": $util.dynamodb.toDynamoDBJson($ctx.args.userId)
}
}
// Query.getUser.response.vtl
$util.toJson($context.result)
2.Query.getPost
userIdを渡すことで、該当するユーザーデータを返すVTLを作成します。
// Query.getPost.request.vtl
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"postId": $util.dynamodb.toDynamoDBJson($ctx.args.postId)
}
}
// Query.getPost.response.vtl
$util.toJson($context.result)
3.Query.listPost
全てのユーザーを取得し、それぞれのユーザーの情報を返すVTLを作成します。
// Query.listPost.request.vtl
{
"version": "2017-02-28",
"operation": "Scan"
}
// Query.listPost.response.vtl
$util.toJson($context.result.items)
4.Mutation.createPost
userIdとその他のキーを持つinputDataを受け取り、createdAtやupdatedAtなどの日時を自動的にDBのフィールドに保存するVTLを作成します。
// Mutation.createPost.request.vtl
{
"version": "2017-02-28",
"operation": "PutItem",
"key": {
"postId": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.args.input.postId, $util.autoId())),
"userId": $util.dynamodb.toDynamoDBJson($ctx.args.input.userId),
"createdAt": $util.dynamodb.toDynamoDBJson($util.time.nowISO8601()),
"updatedAt": $util.dynamodb.toDynamoDBJson($util.time.nowISO8601()),
},
"attributeValues": $util.dynamodb.toMapValuesJson($ctx.args.input),
"condition": {
"expression": "attribute_not_exists(#postId)",
"expressionNames": {
"#postId": "postId"
}
}
}
// Mutation.createPost.response.vtl
$util.toJson($ctx.result)
5.Mutation.deletePost
特定のuserIdを受け取り、該当するデータを削除するためのVTLを作成します。
// Mutation.deleteUser.request.vtl
{
"version": "2017-02-28",
"operation": "DeleteItem",
"key": {
"postId": $util.dynamodb.toDynamoDBJson($ctx.args.input.postId)
}
}
// Mutation.deletePost.response.vtl
$util.toJson($ctx.result)
6.Mutation.updatePost
特定のuserIdを受け取り、nameやemailなどのフィールドのデータを更新するためのVTLを作成します。
// Mutation.updateUser.request.vtl
{
"version": "2017-02-28",
"operation": "PutItem",
"key": {
"postId": $util.dynamodb.toDynamoDBJson($$ctx.args.input.postId)
},
"attributeValues": {
"content": $util.dynamodb.toDynamoDBJson($$ctx.args.input.content),
"userId": $util.dynamodb.toDynamoDBJson($$ctx.args.input.userId),
"updatedAt": $util.dynamodb.toDynamoDBJson($util.time.nowISO8601())
}
}
// Mutation.updatePost.response.vtl
$util.toJson($ctx.result)
このVTLをServerlessFrameworkの環境構築時にAppSyncに反映させるために、serverless.ymlに記述していきます。
custom:
appSync:
name: ${self:service}-${self:provider.stage}
authenticationType: AMAZON_COGNITO_USER_POOLS
userPoolConfig:
awsRegion: ap-northeast-1
defaultAction: ALLOW
region: ap-northeast-1
userPoolId:
Ref: UserPool
mappingTemplatesLocation: mapping-templates
mappingTemplates:
# ------------------------------------------
# Queries
# ------------------------------------------
-
type: Query
field: getPost
dataSource: Post
-
type: Query
field: listPost
dataSource: Post
-
type: Query
field: getUser
dataSource: User
-
type: Query
field: listPost
dataSource: Post
# ------------------------------------------
# Mutations
# ------------------------------------------
-
type: Mutation
field: createPost
dataSource: Post
-
type: Mutation
field: updatePost
dataSource: Post
-
type: Mutation
field: deletePost
dataSource: Post
schema: schema.graphql
このように、typeやfield、dataSourceを指定することでAppSyncのリゾルバーとしてVTLを反映することができます。
ただし、VTLは慣れない言語であるため、学習コストがかかる場合もあります。
最近ではDirect Lambda Resolversがリリースされ、VTLを書かずに済む場面も増えていますが、まだまだVTLを使用する必要がある場面も多いです。
そのため、VTLの学習は重要かもしれませんが、正直なところ書くのが億劫な気持ちもあります。
この記事が気に入ったらサポートをしてみませんか?