AWS Amplify JavaScriptを使ってAWS AppSync APIを作成する場合、 amplify add api した直後はDynamoDBのテーブルが新規作成されます。
既存のDynamoDBを使いたい場合は、 amplify push でAPIをデプロイ後にAppSync Consoleにて内容を編集することもできます。
ただ、手作業になるため
- 同一環境の再現
- 作業ミスの防止
などは難しいです。
コードベースでカスタマイズする方法を探したところ、CustomResourcesを使えば良さそうでした。
RFC: Custom data sources, resolvers, and resources with GraphQL API category. · Issue #574 · aws-amplify/amplify-cli
そこで今回は
の3パターンを試してみた時のメモを残します。
なお、multi-env下で各環境固有のパラメータもCustomResourceに設定しようとしましたが、現状ではできないようです。以下のissueが対応されれば、将来的にはできるようになるかもしれません。
How can you define custom environment-specific variables? · Issue #1366 · aws-amplify/amplify-cli
また、CustomResourcesはCloudFormationの設定ファイルを書く感じとなります。
ただ、CloudFormationの設定ファイルはJSONとYAMLの両方をサポートしていますが、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として追加します。
なお、 Ref や AWS::AppSync::DataSource 、 DynamoDBConfig の詳細については、以下のCloudFrontの公式ドキュメントに記載があります。
- Ref - AWS CloudFormation
- AWS::AppSync::DataSource - AWS CloudFormation
- AWS AppSync DataSource DynamoDBConfig - AWS CloudFormation
{ "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のドキュメントは以下です。
- AWS AppSync リゾルバー PipelineConfig - AWS CloudFormation
- AWS::AppSync::FunctionConfiguration - AWS 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