Closed11
AWS CloudFormationでAWS AppSync上にGraphQL APIを構築したい(HTTPデータソース)
AWS AppSync JavaScript ResolverでHTTPデータソース(REST API)からデータを収集すると同じ仕様のAPIをCloudFormationで作りたい
参考資料
- https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/AWS_AppSync.html
- https://www.ragate.co.jp/blog/articles/6070
- https://dev.classmethod.jp/articles/tsnote-cloudformation-include-external-parameter-file/
- https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/create-reusable-transform-function-snippets-and-add-to-your-template-with-aws-include-transform.html
備考
- CloudFormationは全く触ったことない
- AppSyncのサンプルAPIがCloudFormationでシャッと作れるのが体験よかったので触っておきたい
- GraphQLのスキーマをテンプレートファイルに埋め込むのではなく外部ファイルに出せるとうれしい
やってみて
- CloudFormationそのものの取っつきにくさがある
- テンプレートを書くときのGetting Started的な情報が意外と出てこない
-
https://www.ragate.co.jp/blog/articles/6070 のテンプレートをコピペして1つずつ確認しながら作った
- 先頭に
AWSTemplateFormatVersion: '2010-09-09'
がなかったので追加した
- 先頭に
-
https://www.ragate.co.jp/blog/articles/6070 のテンプレートをコピペして1つずつ確認しながら作った
- デザイナーのUXがよくない
- 結局リファレンスを見ながらプロパティを手書きする必要があり、何のためにGUIになっているのかがわからない
- テンプレートをS3に置いて~みたいな記事を複数見たけど結局ファイルをアップロードするのが一番ストレートでよかった
- テンプレートを書くときのGetting Started的な情報が意外と出てこない
- AppSyncの設定自体はいったんコンソールで作ったものを転記していくだけだったので特に問題なかった
- スキーマやリゾルバの実装をテンプレート内に書く必要があるので、IaCとしては微妙さを感じる
- Gitでバージョン管理してCI/CDしたい
- JSとGraphQLのコードは外に出してそれぞれlinterを通したいけどそれができない
- S3に置けば外に出せるけど微妙
- GitHubからS3にデプロイして~というのは可能だと思うけど...
- テンプレートも一緒にS3にデプロイするようにしてGitHub -> S3 -> CloudFormationとすればよい?
最終成果物
スキーマを外部ファイルで管理したい
外部ファイルはAWS::Include transformで埋め込むことができるけどS3に置いておく必要がある
テンプレートファイルをGitHubで管理したいのでこれだと微妙
こんなことしなくてもDefinitionS3Location
でS3に置いたスキーマファイルを読み込めるらしい
参考 https://www.ragate.co.jp/blog/articles/6070#:~:text=DefinitionS3Location
リゾルバをJavaScriptで書きたい
AWS::AppSync::Resolver
にCode
で指定する
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0
が必要
テンプレート
AWSTemplateFormatVersion: '2010-09-09'
Resources:
GraphQLApi:
Type: AWS::AppSync::GraphQLApi
Properties:
AuthenticationType: API_KEY
Name: AppSync Playground(cfn)
ApiKey:
Type: AWS::AppSync::ApiKey
Properties:
ApiId:
Fn::GetAtt: [GraphQLApi, ApiId]
Expires: 1677510000 # 2023年2月28日 火曜日 00:00:00 GMT+09:00
DataSource1:
Type: AWS::AppSync::DataSource
Properties:
ApiId:
Fn::GetAtt: [GraphQLApi, ApiId]
Name: catstronauts_rest_api
Type: HTTP
HttpConfig:
Endpoint: https://odyssey-lift-off-rest-api.herokuapp.com
FunctionConfiguration1:
Type: AWS::AppSync::FunctionConfiguration
Properties:
ApiId:
Fn::GetAtt: [GraphQLApi, ApiId]
DataSourceName:
Fn::GetAtt: [DataSource1, Name]
FunctionVersion: '2023-02-14'
Name: getTracksForHome
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0
Code: |
import { util } from '@aws-appsync/utils';
export function request(ctx) {
return {
method: 'GET',
params: {
headers: { 'Content-Type': 'application/json' },
},
resourcePath: '/tracks',
};
}
export function response(ctx) {
const { error, result } = ctx;
if (error) {
ctx.stash.errors = ctx.stash.errors ?? [];
ctx.stash.errors.push(ctx.error);
return util.appendError(error.message, error.type, result);
}
if (result.statusCode === 200) {
return JSON.parse(result.body);
} else {
return util.appendError(result.body, result.statusCode);
}
}
FunctionConfiguration2:
Type: AWS::AppSync::FunctionConfiguration
Properties:
ApiId:
Fn::GetAtt: [GraphQLApi, ApiId]
DataSourceName:
Fn::GetAtt: [DataSource1, Name]
FunctionVersion: '2023-02-14'
Name: getAuthor
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0
Code: |
import { util } from '@aws-appsync/utils';
export function request(ctx) {
return {
method: 'GET',
params: {
headers: { 'Content-Type': 'application/json' },
},
resourcePath: `/author/${ctx.source.authorId}`,
};
}
export function response(ctx) {
const { error, result } = ctx;
if (error) {
ctx.stash.errors = ctx.stash.errors ?? [];
ctx.stash.errors.push(ctx.error);
return util.appendError(error.message, error.type, result);
}
if (result.statusCode === 200) {
return JSON.parse(result.body);
} else {
return util.appendError(result.body, result.statusCode);
}
}
GraphQLSchema:
Type: AWS::AppSync::GraphQLSchema
Properties:
ApiId:
Fn::GetAtt: [GraphQLApi, ApiId]
Definition: |
type Author {
id: ID!
name: String!
photo: String
}
type Query {
tracksForHome: [Track!]!
}
type Track {
id: ID!
title: String!
author: Author!
thumbnail: String
length: Int
modulesCount: Int
}
Resolver1:
Type: AWS::AppSync::Resolver
Properties:
ApiId:
Fn::GetAtt: [GraphQLApi, ApiId]
TypeName: 'Query'
FieldName: 'tracksForHome'
Kind: PIPELINE
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0
Code: |
import {util} from '@aws-appsync/utils';
export function request(ctx) {
return {};
}
export function response(ctx) {
return ctx.prev.result;
}
PipelineConfig:
Functions: [Fn::GetAtt: [FunctionConfiguration1, FunctionId]]
Resolver2:
Type: AWS::AppSync::Resolver
Properties:
ApiId:
Fn::GetAtt: [GraphQLApi, ApiId]
TypeName: 'Track'
FieldName: 'author'
Kind: PIPELINE
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0
Code: |
import {util} from '@aws-appsync/utils';
export function request(ctx) {
return {};
}
export function response(ctx) {
return ctx.prev.result;
}
PipelineConfig:
Functions: [Fn::GetAtt: [FunctionConfiguration2, FunctionId]]
CloudFormationコンソールからスタックの作成 > 新しいリソースを使用(標準)で作成した
スタック名のみ編集してそれ以外はデフォルトのまま
- 2回テンプレートのエラーでスタックを作り直した
-
-
ApiKey
のExpires
を参考にした記事の値のまま設定してしまった(エポック秒なのでちゃんと未来の日付で設定しないとダメ)
-
-
- リゾルバの
Runtime
はAppSyncRuntimeオブジェクトで設定しないといけないのにリファレンスを斜め読みして"APPSYNC_JS"
のみ文字列で指定していた
- リゾルバの
-
- 作成に失敗したスタックはスタックの更新が行えないので、スタックを削除 -> 再度作成する必要があった
- エラーはコンソールのイベントタグに出るメッセージを見てリファレンスを確認したら特に困ることなく解消できた
雑感
- 作りたい構成に近いサンプルがあれば編集してサッと作れるのはよい
- リゾルバのJSコードをYAMLに書くのが微妙
- AppSyncのコンソールで編集してリソースのインポートで更新するのがよさそう?未検証
- S3に置けば別管理できるけど、Git管理できないのでやる意味がない
- 特に認証とか考えなければAppSyncのリソースだけ定義すればよいのでシンプル
このスクラップは2023/02/14にクローズされました