AWS AppSync + Amplify JavaScript + CustomResourcesで、既存のDynamoDBなどをDatasourceとしたリゾルバーを作成する

AWS Amplify JavaScriptを使ってAWS AppSync APIを作成する場合、 amplify add api した直後はDynamoDBのテーブルが新規作成されます。

既存のDynamoDBを使いたい場合は、 amplify pushAPIをデプロイ後にAppSync Consoleにて内容を編集することもできます。

ただ、手作業になるため

  • 同一環境の再現
  • 作業ミスの防止

などは難しいです。

 
コードベースでカスタマイズする方法を探したところ、CustomResourcesを使えば良さそうでした。
RFC: Custom data sources, resolvers, and resources with GraphQL API category. · Issue #574 · aws-amplify/amplify-cli

 
そこで今回は

  • 既存のDynamoDBを使って、ユニットリゾルバーを作成
  • 既存のDynamoDBを使って、パイプラインリゾルバーを作成
  • Noneデータソースのリゾルバーを作成

の3パターンを試してみた時のメモを残します。

 
なお、multi-env下で各環境固有のパラメータもCustomResourceに設定しようとしましたが、現状ではできないようです。以下のissueが対応されれば、将来的にはできるようになるかもしれません。
How can you define custom environment-specific variables? · Issue #1366 · aws-amplify/amplify-cli

 
また、CustomResourcesはCloudFormationの設定ファイルを書く感じとなります。

ただ、CloudFormationの設定ファイルはJSONYAMLの両方をサポートしていますが、Amplifyで作成する場合はJSONのみサポートしています。

そのため、YAMLで記述したCustomResourceを使おうとすると、以下のエラーが発生します。

Yaml is not yet supported. Please convert the CloudFormation stack ExistsDynamoDB.yaml to json.

 
目次

 

環境

   
また、AppSync API環境は、以下の方法で作成したものとします。

amplify init

$ amplify init
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project infra_by_amplify
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using none
? Source Directory Path:  src
? Distribution Directory Path: dist
? Build Command:  npm run-script build
? Start Command: npm run-script start
Using default provider  awscloudformation

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use default

CREATE_IN_PROGRESS infrabyamplify-dev-xxx AWS::CloudFormation::Stack ... User Initiated             
CREATE_IN_PROGRESS DeploymentBucket                  AWS::S3::Bucket ...
CREATE_IN_PROGRESS AuthRole                          AWS::IAM::Role ...
CREATE_IN_PROGRESS UnauthRole                        AWS::IAM::Role ...

 
amplify add api

$ amplify add api

? Please select from one of the below mentioned services GraphQL
? Provide API name: InfraAPI
? Choose an authorization type for the API API key
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? No

# Schemaなしは選べなかったので、やむを得ず MyType というtypeを作成
? Provide a custom type name MyType

 

既存のDynamoDBを使って、ユニットリゾルバーを作成

AppSyncのリゾルバーには

  • ユニットリゾルバー
  • パイプラインリゾルバー

の2つがあります。
システムの概要とアーキテクチャ - AWS AppSync

まずは、既存のDynamoDBをDataSourceとしたユニットリゾルバーを作成してみます。

今回は Board という既存のDynamoDBを使います。スキーマとデータは以下の通りです。

key author content
1 baz egg

 

schema.graphqlの変更

まずは、デフォルトで作成されたSchemaを変更します。

project_root/amplify/backend/api/infraAPI/schema.graphql を開き、 Query listBoards 用のSchemaに変更します。今回はDynamoDBからデータを取得するQueryを定義します。

なお、DynamoDBは既に存在しているため、 @model ディレクティブは不要です。

type Board {
    key: String
    author: String
    content: String
}

type BoardConnection {
    items: [Board]
    nextToken: String
}

type Query {
    listBoards(limit: Int, nextToken: String): BoardConnection
}

 

stacksに、CustomResourcesを追加

続いて、

  • 既存のDynamoDBをDatasourceとして使う
  • Schemaに対するユニットリゾルバーを作成する

を行うため、 project_root/amplify/backend/api/infraAPI/stacks の中に ExistsDynamoDB.json ファイルを作成します。

なお、同じディレクトリには CustomResources.json があります。このファイルに追記しても良いですし、別ファイルとして作成しても良いです。

今回は、 CustomResources.json をベースに必要な項目を追加した ExistsDynamoDB.json となります。

 

Parametersの下に ServiceRoleARN 用の項目を追加

まずは、使い回ししやすくするため、DynamoDBを操作するためのServiceRoleARNを外部から渡せるようにします。

Parameters の下に、BoardDynamoDBServiceRoleArn を追加します。

{
  "AWSTemplateFormatVersion": "2010-09-09",
  ...
  "Parameters": {
    // 以下を追加
    "BoardDynamoDBServiceRoleArn": {
      // 外部ファイルから文字列で受け取るため、 "String" を指定
      "Type": "String",
      "Description": "ServiceRoleArnOfDynamoDB"
    }
    ...
}

 

Resourcesの下に、DataSourceを追加

今回はDynamoDBの Board テーブルをDataSourceとして追加します。

なお、 RefAWS::AppSync::DataSourceDynamoDBConfig の詳細については、以下のCloudFrontの公式ドキュメントに記載があります。

{
  "AWSTemplateFormatVersion": "2010-09-09",
  ...
  "Parameters": {
    "AppSyncApiId": {
      "Type": "String",
      "Description": "The id of the AppSync API associated with this project."
    },
    ...
    "BoardDynamoDBServiceRoleArn": {
      "Type": "String",
      "Description": "ServiceRoleArnOfDynamoDB"
    }
  },
  "Resources": {
    // 以下を追加
    "DataSourceOfExistsDynamoDB": {
      // DataSourceの定義をするので、固定値
      "Type": "AWS::AppSync::DataSource",
      "Properties": {
        // どこのAPIに紐付けるか
        // "Ref": "AppSyncApiId"で、上のParametersで指定 AppSyncApiId の値を渡す
        "ApiId": {
          "Ref": "AppSyncApiId"
        },
        // 任意の名前、AppSync Console - Data Source のNameとなる
        "Name": "BoardDataSource",
        // DynamoDBを使うので固定
        "Type": "AMAZON_DYNAMODB",
        // DynamoDBを使うので必須
        // 上のParametersで指定した BoardDynamoDBServiceRoleArn の値を渡す
        "ServiceRoleArn": {
          "Ref": "BoardDynamoDBServiceRoleArn"
        },
        // DynamoDBの設定
        "DynamoDBConfig": {
          "TableName": "Board",
          "AwsRegion" : {
            "Ref": "AWS::Region"
          }
        }
      }
    },
}

 

Resourcesの下に、ユニットリゾルバーを追加

DataSourceができたので、次はQueryとDataSourceをつなぐリゾルバーを追加します。

関係するCloudFormationのドキュメントはこちらです。

 

{
  "AWSTemplateFormatVersion": "2010-09-09",
  ...
  "Parameters": {
    "AppSyncApiId": { ...
    },
    ...
    "S3DeploymentBucket": { ...
    },
    "S3DeploymentRootKey": { ...
    },
    ...
  },
  "Resources": {
    "DataSourceOfExistsDynamoDB": {
        ...
        "Name": "BoardDataSource",
        ...
        }
      }
    },
    // 以下を追加
    "ListBoardsResolver": {
      // リゾルバーを追加するので固定値
      "Type": "AWS::AppSync::Resolver",
      "Properties": {
        // リゾルバーを作成するAPI
        "ApiId": {
          "Ref": "AppSyncApiId"
        },
        // リゾルバーのDataSource。名前を参照するため、先ほど追加した "DataSourceOfExistsDynamoDB" の "Name" を使う
        "DataSourceName": {
          "Fn::GetAtt": [
            "DataSourceOfExistsDynamoDB",
            "Name"
          ]
        },
        // リゾルバーのType。今回はQuery用のリゾルバー
        "TypeName": "Query",
        // リゾルバーをAttachするQuery名。Schemaに書いたものを指定
        "FieldName": "listBoards",
        // リクエストマッピングテンプレートの指定。直接書くこともできるが、今回はS3にテンプレートをアップロードして、そちらを参照する
        // 書き方は定形
        "RequestMappingTemplateS3Location": {
          "Fn::Sub": [
            // 後で resolvers ディレクトリに用意するリクエストマッピングテンプレートのファイル名を指定
            "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.listBoards.req.vtl",
            {
              "S3DeploymentBucket": {
                "Ref": "S3DeploymentBucket"
              },
              "S3DeploymentRootKey": {
                "Ref": "S3DeploymentRootKey"
              }
            }
          ]
        },
        // 同じく、レスポンスマッピングテンプレートを指定
        "ResponseMappingTemplateS3Location": {
          "Fn::Sub": [
            // 同様に、 resolvers ディレクトリに用意するリクエストマッピングテンプレートのファイル名を指定
            "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.listBoards.res.vtl",
            {
              "S3DeploymentBucket": {
                "Ref": "S3DeploymentBucket"
              },
              "S3DeploymentRootKey": {
                "Ref": "S3DeploymentRootKey"
              }
            }
          ]
        }
      }
    }
...

 

resolversに、マッピングテンプレートを作成

次に、 project_root/amplify/backend/api/infraAPI/resolvers の中に、リクエスト/レスポンスマッピングテンプレートを作成します。

 

リクエスマッピングテンプレートを作成

stacks/ExistsDynamoDB.json で指定したファイル名 Query.listBoards.req.vtl にてリクエスマッピングテンプレートを作成します。

今回はスキャンのテンプレートを作成します。
https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/resolver-mapping-template-reference-dynamodb.html#aws-appsync-resolver-mapping-template-reference-dynamodb-scan

{
  "version": "2017-02-28",
  "operation": "Scan",
  "limit": $util.defaultIfNull($ctx.args.limit, 20),
  "nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null)),
}

 

レスポンスマッピングテンプレートを作成

同じく、 stacks/ExistsDynamoDB.json で指定したファイル名 Query.listBoards.res.vtl にてレスポンスマッピングテンプレートを作成します。

今回は取得した結果をそのまま返します。

$util.toJson($context.result)

 

parameters.jsonの追加

最後に、ServiceRoleARN をCustomResourcesに渡すため、今回は project_root/amplify/backend/api/InfraAPI/paramters.json に追加します。

{
    "AppSyncApiName": "InfraAPI",
    "DynamoDBBillingMode": "PAY_PER_REQUEST",
    // Boardテーブルを操作可能なServiceRoleのARNを追加
    "BoardDynamoDBServiceRoleArn": "arn:aws:iam::xxx:role/service-role/appsync-xxx-Board",
}

 

amplify push

ここまでで作業が終わったため、AppSync APIを作成します。

$ amplify push

Current Environment: dev

| Category | Resource name | Operation | Provider plugin   |
| -------- | ------------- | --------- | ----------------- |
| Api      | InfraAPI      | Create    | awscloudformation |
? Are you sure you want to continue? Yes

GraphQL schema compiled successfully.
Edit your schema at path/to/infra_by_amplify/amplify/backend/api/InfraAPI/schema.graphql
or place .graphql files in a directory at path/to/infra_by_amplify/amplify/backend/api/InfraAPI/schema

# APIを新しく作成する
? Do you want to generate code for your newly created GraphQL API Yes

# JavaScript用のコードを生成する
? Choose the code generation language target javascript

# 生成するコードを置くディレクトリなどを指定する
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js

# JavaScript用のQueryコードを自動生成する
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes

# ネストはデフォルト(2)のまま
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2

 
pushが終わると、AppSync APIが新規作成されています。AppSync Consoleで自分が書いた内容と一致するか確認します。

 

動作確認

最後に、AppSync ConsoleのQueriesを使って動作確認をします。

Queriesに

query listBoards {
  listBoards {
    items {
      key
      author
      content
    }
    nextToken
  }
}

と記載し、

{
  "data": {
    "listBoards": {
      "items": [
        {
          "key": "1ad5dcf8-ca8b-4eba-9193-5c3c37371644",
          "author": "baz",
          "content": "egg"
        }
      ],
      "nextToken": null
    }
  }
}

となれば、無事に作成できています。

 

既存のDynamoDBを使って、パイプラインリゾルバーを作成

続いて、既存のDynamoDBを使って、パイプラインリゾルバーを作成してみます。

パイプラインリゾルバーをAppSync Console上で作成するのは以前試しました。 AWS AppSyncのPipeline Resolverを使って、複数のDynamoDBの値をマージして返すAPIを作成してみた - メモ的な思考的な

そこで今回は、上記記事と同じ内容でパイプラインリゾルバを作成します。

なお、DynamoDBの状況は以下の通りです。

Blog table

id (key) title author_id
1 ham 100
2 spam 200

 
 
Author table

Blog tableの author_id に紐づく id を持つテーブルです。

id (key) name
100 foo
200 bar

 

schema.graphqlへの追記
type BlogWithAuthor {
    id: String!
    title: String
    author_id: String
    author_name: String
}

type Query {
    ...
    getByPipeline(id: String!): BlogWithAuthor
}

 

stacks/PipelineResolver.jsonの作成

ユニットリゾルバーとは別のファイル PipelineResolver.json に、CustomResources を追加していきます。

ユニットリゾルバーとは異なる部分をメインにコメントを入れてあります。また、関係するCloudFormationのドキュメントは以下です。

{
  "AWSTemplateFormatVersion": "2010-09-09",
  ...
  "Parameters": {
    ...
    // DynamoDB "Author" を操作するための ServiceRoleArn を渡すためのパラメータ
    "AuthorDynamoDBServiceRoleArn": {
      "Type": "String",
      "Description": "DynamoDBServiceRoleArn of Author table"
    },
    // DynamoDB "Blog" を操作するための ServiceRoleArn を渡すためのパラメータ
    "BlogDynamoDBServiceRoleArn": {
      "Type": "String",
      "Description": "DynamoDBServiceRoleArn of Blog table"
    }
  },
  "Resources": {
    // 既存のDynamoDBテーブルその1
    "AuthorTable": {
      "Type": "AWS::AppSync::DataSource",
      "Properties": {
        "ApiId": {
          "Ref": "AppSyncApiId"
        },
        "Name": "AuthorDataSource",
        "Type": "AMAZON_DYNAMODB",
        "ServiceRoleArn": {
          "Ref": "AuthorDynamoDBServiceRoleArn"
        },
        "DynamoDBConfig": {
          "TableName": "Author",
          "AwsRegion" : {
            "Ref": "AWS::Region"
          }
        }
      }
    },
    // 既存のDynamoDBテーブルその1
    "BlogTable": {
      "Type": "AWS::AppSync::DataSource",
      "Properties": {
        "ApiId": {
          "Ref": "AppSyncApiId"
        },
        "Name": "BlogDataSource",
        "Type": "AMAZON_DYNAMODB",
        "ServiceRoleArn": {
          "Ref": "BlogDynamoDBServiceRoleArn"
        },
        "DynamoDBConfig": {
          "TableName": "Blog",
          "AwsRegion" : {
            "Ref": "AWS::Region"
          }
        }
      }
    },
    "PipeLineResolverOfAuthorAndBlog": {
      "Type": "AWS::AppSync::Resolver",
      "Properties": {
        "ApiId": {
          "Ref": "AppSyncApiId"
        },
        "TypeName": "Query",
        "FieldName": "getByPipeline",
        // パイプラインリゾルバーの場合、 `Kind: "PIPELINE"` が必要
        "Kind": "PIPELINE",
        // パイプラインリゾルバーの設定
        "PipelineConfig": {
          // パイプラインリゾルバーで使用するFunctionの "FunctionId" を、使用順で定義する
          "Functions": [
            {
              "Fn::GetAtt" : [ "FunctionBlog" , "FunctionId" ]
            },
            {
              "Fn::GetAtt" : [ "FunctionBlogWithAuthor" , "FunctionId" ]
            }
          ]
        },
        "RequestMappingTemplateS3Location": {
          "Fn::Sub": [
            "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.PipeLineResolverOfAuthorAndBlog.req.vtl",
            {
              "S3DeploymentBucket": {
                "Ref": "S3DeploymentBucket"
              },
              "S3DeploymentRootKey": {
                "Ref": "S3DeploymentRootKey"
              }
            }
          ]
        },
        "ResponseMappingTemplateS3Location": {
          "Fn::Sub": [
            "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.PipeLineResolverOfAuthorAndBlog.res.vtl",
            {
              "S3DeploymentBucket": {
                "Ref": "S3DeploymentBucket"
              },
              "S3DeploymentRootKey": {
                "Ref": "S3DeploymentRootKey"
              }
            }
          ]
        }
      }
    },
    // Blogテーブルからデータを取得するFunction
    "FunctionBlog": {
      // Functionの定義なので固定
      "Type": "AWS::AppSync::FunctionConfiguration",
      "Properties": {
        // Functionを含むAppSync APIのID
        "ApiId": {
          "Ref": "AppSyncApiId"
        },
        // Functionの名前
        "Name": "GetBlog",
        "Description": "Get Blog Table",
        // FunctionのDataSource
        "DataSourceName": {
          "Fn::GetAtt" : [ "BlogTable" , "Name" ]
        },
        "FunctionVersion": "2018-05-29",
        "RequestMappingTemplateS3Location": {
          "Fn::Sub": [
            "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.GetBlog.req.vtl",
            {
              "S3DeploymentBucket": {
                "Ref": "S3DeploymentBucket"
              },
              "S3DeploymentRootKey": {
                "Ref": "S3DeploymentRootKey"
              }
            }
          ]
        },
        "ResponseMappingTemplateS3Location": {
          "Fn::Sub": [
            "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.GetBlog.res.vtl",
            {
              "S3DeploymentBucket": {
                "Ref": "S3DeploymentBucket"
              },
              "S3DeploymentRootKey": {
                "Ref": "S3DeploymentRootKey"
              }
            }
          ]
        }
      }
    },
    // Authorテーブルからデータを取得するFunction
    "FunctionBlogWithAuthor": {
      "Type": "AWS::AppSync::FunctionConfiguration",
      "Properties": {
        "ApiId": {
          "Ref": "AppSyncApiId"
        },
        "Name": "GetBlogWithAuthor",
        "Description": "Merge Blog and Author",
        "DataSourceName": {
          "Fn::GetAtt" : [ "AuthorTable" , "Name" ]
        },
        "FunctionVersion": "2018-05-29",
        "RequestMappingTemplateS3Location": {
          "Fn::Sub": [
            "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.GetBlogWithAuthor.req.vtl",
            {
              "S3DeploymentBucket": {
                "Ref": "S3DeploymentBucket"
              },
              "S3DeploymentRootKey": {
                "Ref": "S3DeploymentRootKey"
              }
            }
          ]
        },
        "ResponseMappingTemplateS3Location": {
          "Fn::Sub": [
            "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.GetBlogWithAuthor.res.vtl",
            {
              "S3DeploymentBucket": {
                "Ref": "S3DeploymentBucket"
              },
              "S3DeploymentRootKey": {
                "Ref": "S3DeploymentRootKey"
              }
            }
          ]
        }
      }
    }
  },
  ...
}

 

マッピングテンプレートを作成

今回は、1つのパイプラインリゾルバーと、2つのFunctionを作成したため、それぞれのマッピングテンプレートを作成します。

 

パイプラインリゾルバー用

resolvers ディレクトリの中に、Before mapping template Query.PipeLineResolverOfAuthorAndBlog.req.vtl を作成します。Queryの引数として id を受け取ります。

#set($result = { "id": $ctx.args.id })
$util.toJson($result)

 
after mapping template Query.PipeLineResolverOfAuthorAndBlog.res.vtl はこちら。Functionの結果をそのまま返します。

$util.toJson($ctx.result)

 

GetBlog Function用

リクエスマッピングテンプレート Query.GetBlog.req.vtl です。今回は GetItem を使います。
https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/resolver-mapping-template-reference-dynamodb.html#aws-appsync-resolver-mapping-template-reference-dynamodb-getitem

また、 $context の短縮形 $ctx を使っています。

{
    "operation": "GetItem",
    "key": {
        "id": $util.dynamodb.toDynamoDBJson($ctx.args.id),
    }
}

 
レスポンスマッピングテンプレート Query.GetBlog.res.vtl です。エラーがあればエラーを返します。

## Raise a GraphQL field error in case of a datasource invocation error
#if($ctx.error)
    $util.error($ctx.error.message, $ctx.error.type)
#end
## Pass back the result from DynamoDB. **
$util.toJson($ctx.result)

 

GetBlogWithAuthor Function用

リクエスマッピングテンプレート Query.GetBlogWithAuthor.req.vtl です。

{
    "operation": "GetItem",
    "key": {
        "id": $util.dynamodb.toDynamoDBJson($ctx.prev.result.author_id),
    }
}

 
リクエスマッピングテンプレート Query.GetBlogWithAuthor.res.vtl です。

今回はGetBlogで取得したデータに追加していますが、GetBlogWithAuthorのデータにGetBlogのデータをマージすることもできます。

## Raise a GraphQL field error in case of a datasource invocation error
#if($ctx.error)
    $util.error($ctx.error.message, $ctx.error.type)
#end
## Pass back the result from DynamoDB. **

## GetBlogの結果に、author_nameとして今回取得したAuthorテーブルのnameの値をマージ
$util.qr($ctx.prev.result.put("author_name", $ctx.result.name))

## マージしたデータを返す
$util.toJson($ctx.prev.result)

 

parameters.jsonへの追加

今回もDynamoDBの操作用ServiceRoleを設定していますので、 parameters.json へも追加します。

{
    // 追加
    "AuthorDynamoDBServiceRoleArn": "arn:aws:iam::xxx:role/service-role/appsync-xxx-Author",
    "BlogDynamoDBServiceRoleArn": "arn:aws:iam::xxx:role/service-role/appsync-xxx-Blog"
}

 
以上で作成が終わりました。

 

動作確認

ユニットリゾルバーと同様に、AppSync ConsoleのQueriesにて動作確認をします。

query GetByPipeline($id: String!) {
  getByPipeline(id: $id) {
    id
    title
    author_id
    author_name
  }
}

QUERY VARIABLES にも以下を追加します。

{
  "id": "1"
}

実行すると、以下の結果が得られます。

{
  "data": {
    "getByPipeline": {
      "id": "1",
      "title": "ham",
      "author_id": "100",
      "author_name": "foo"
    }
  }
}

 

Noneデータソースのリゾルバーを作成

今まで、既存DynamoDBをDatasourceとして使ってみました。

ただ、AppSyncではDynamoDB以外にもDatasourceとして扱えるものがあり、それらもCustomResourceで生成できます。
リゾルバーのマッピングテンプレートリファレンス - AWS AppSync

 
今回は、ローカルリゾルバーに向いている None データソースのリゾルバーを作成してみます。
チュートリアル : ローカルリゾルバー - AWS AppSync

 

schema.graphql の追加

NoneデータソースのQueryを追加します。

type NoneResponse {
    comment: String
}

type Query {
    ...
    getNoneDatasource: NoneResponse
}

 

stacks/ResolverWithNoneDatasource.json の追加
{
  "AWSTemplateFormatVersion": "2010-09-09",
  ...
  "Resources": {
    "NoneSourceOfGetContext": {
      "Type": "AWS::AppSync::DataSource",
      "Properties": {
        "ApiId": {
          "Ref": "AppSyncApiId"
        },
        "Name": "NoneSource",
        // TypeをNoneにする
        "Type": "NONE"
      }
    },
    "GetContextResolver": {
      "Type": "AWS::AppSync::Resolver",
      "Properties": {
        "ApiId": {
          "Ref": "AppSyncApiId"
        },
        "DataSourceName": {
          "Fn::GetAtt": [
            "NoneSourceOfGetContext",
            "Name"
          ]
        },
        "TypeName": "Query",
        "FieldName": "getNoneDatasource",
        "RequestMappingTemplateS3Location": {
          "Fn::Sub": [
            "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.getNoneDatasource.req.vtl",
            {
              "S3DeploymentBucket": {
                "Ref": "S3DeploymentBucket"
              },
              "S3DeploymentRootKey": {
                "Ref": "S3DeploymentRootKey"
              }
            }
          ]
        },
        "ResponseMappingTemplateS3Location": {
          "Fn::Sub": [
            "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.getNoneDatasource.res.vtl",
            {
              "S3DeploymentBucket": {
                "Ref": "S3DeploymentBucket"
              },
              "S3DeploymentRootKey": {
                "Ref": "S3DeploymentRootKey"
              }
            }
          ]
        }
      }
    }
  },
  ...
}

 

リクエスト/レスポンスマッピングテンプレートを作成
リクエスマッピングテンプレート

resolvers/Query.getNoneDatasource.req.vtl を作成します。

{
    "version": "2017-02-28",
    "payload": {
        "comment": "Hello, world!"
   }
}

 

レスポンスマッピングテンプレート

resolvers/Query.getNoneDatasource.res.vtl を作成します。

$utils.toJson($context.result)

 

動作確認

こちらもAppSync Consoleで動作を確認します。

query GetNoneDatasource {
  getNoneDatasource {
    comment
  }
}

を実行すると

{
  "data": {
    "getNoneDatasource": {
      "comment": "Hello, world!"
    }
  }
}

の結果が得られました。

 

ソースコード

Githubに上げました。ディレクトinfra_by_amplify の中が今回のファイルです。
https://github.com/thinkAmi-sandbox/AWS_AppSync_Amplify-sample