概要
AWS Amplify Advent Calendar 2019、19日目はAWS Amplifyの新機能MockingでGraphQL API開発スピードを爆上げする方法について書いていきます!
Amplify Mockingとは
2019.08.07に発表されたAmplify CLIの新(というにはいささか時間が経ってしまった)機能です。$ amplify pushはAWS CloudFormationのスタックの参照と変更を行うため、それなりに時間がかかってしまいます。$ amplify mockコマンドを使用すると、$ amplify pushでクラウドリソースに変更反映する前に、変更後の動作確認をローカル環境で行うことが可能です。
現在Mockingが提供されているCategoryは、以下の3つです。
- 
api(GraphQL): AWS AppSync + Amazon DynamoDB - 
storage(Content): S3 - 
function: AWS Lambda 
storageとfunctionのMockingについては参考資料の共有に留め、本記事ではAPI(GraphQL)のMockingについて詳しく見ていきます。
Amplify Mock Commands
$ amplify mock
$ amplify mock api
$ amplify mock storage
$ amplify mock function <functionname>
amplify mockに続いてMockしたいCategoryを続けることでMockすることができます。functionは一つのアプリケーションに複数のfunctionを作成することができるため、Mockしたいfunctionを明示的に渡します。Categoryを指定せずamplify mockを実行した場合は、apiとstorageのMockが始まります。
想定読者
$ amplify add apiしてAWS AppSyncいじってるけど、毎回amplify pushの時間待つのだるいなぁ、とお考えの悩めるあなたに捧げます。途中Reactが出てきますが、一切触らないのでReactの知識は必要ありません。
動作確認環境
- react 16.12.0
 - @aws-amplify/cli 4.5.0
 
参考資料
いくつか参考にした資料を載せておきます。Mockingを試すだけであれば、一番下の公式ドキュメントが早いです。
- [Mocking and Testing Serverless APIs with AWS Amplify - AWS Online Tech Talks - YouTube] (https://www.youtube.com/watch?v=OxrHplxZ8BA)
 - New – Local Mocking and Testing with the Amplify CLI | AWS News Blog
 - Developing and testing GraphQL APIs, Storage and Functions with Amplify Framework Local Mocking features | AWS Mobile Blog
 - https://aws-amplify.github.io/docs/cli-toolchain/quickstart?sdk=js#mocking-and-testing
 - https://aws-amplify.github.io/docs/cli-toolchain/usage?sdk=js#mocking-and-testing
 
検証用アプリケーションの準備
React App
本手順ではReactで進めますが、一切コードをいじらないので他のフレームワーク(Vue, Angular,Ionic)でアプリを作っていただいても大丈夫です。
$ npx create-react-app amplify-react
$ cd amplify-react
$ npm start
ブラウザが立ち上がり、おなじみのスタートページが現れます。
Amplifyの初期設定
amplify initでAmplifyの初期設定を行います。
amplify configureは一度実行している想定です。(amplify configureがまだの方はコチラ)
${profile name}にはご自身が使用されているAmplify用のAWS Profile名を入力してください。
$ amplify init
? Enter a name for the project amplify-react
? Enter a name for the environment mocktest
? Choose your default editor: Vim (via Terminal, Mac OS only)
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using react
? Source Directory Path:  src
? Distribution Directory Path: build
? 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 ${profile name}
GraphQL API作成
$ amplify add apiでMockingの検証に使用するBackendを設定していきましょう。
ポイントとしては一度API KEYでセットアップした上で、追加でAmazon Cognito User PoolとAWS IAMを利用した認証を追加しています。これは後ほど@authを利用した際のMockingを行うためです。画像を参考に設定してみてください。
準備完了です!Mockingを実際に使ってみましょう。
API Mocking
API Mockingを試してみよう!
先ほどadd apiしたAppSyncのschema.graphqlを編集しましょう。
type Todo 
@model 
@key(fields: ["id"])
@auth(rules: [
  {allow: owner, provider: userPools, operations: [create, read, update, delete]},
  {allow: private, provider: userPools, operations: [read]},
  {allow: public, provider: apiKey,operations: [read]},
])
{
  id: ID!
  name: String!
  owner: String!
  description: String
  updatedAt: AWSDatetime
  createdAt: AWSDatetime
}
@authについての解説は拙著Amplify CLI GraphQL TransformとディレクティブでAppSync+DynamoDBをいじってみよう!(@model @auth, @key) - Qiitaをご覧ください。上記のschema.graphqlにおける@authは、以下の認可を実現します。
- Cognito User Poolで認証したユーザーに次のアクションを許可
- Todoのcreate
 - 自身が作成したアイテムのread, update, delete
 - 他の人が作成したアイテムのread
 
 - AppSyncのAPI_KEYで認証したユーザーにはreadのみを許可
 
ではこの設定をクラウドのリソースに反映する$ amplify push...の前に!$ amplify mock apiしてみましょう。
$ amplify mock api
Failed to start API Mock endpoint Error: Type "AWSDatetime" not found in document.
AWSDatetime型なんて見つからないよ、というエラーが表示されました。AppSyncのスカラー型によれば、正しくはAWSDateTimeです。うっかり間違えてしまいました。schema.graphqlを編集しましょう。
type Todo 
@model 
@key(fields: ["id"])
@auth(rules: [
  {allow: owner, provider: userPools, operations: [create, read, update, delete]},
  {allow: private, provider: userPools, operations: [read]},
  {allow: public, provider: iam, operations: [read]},
  {allow: public, provider: apiKey,operations: [read]},
])
{
  id: ID!
  name: String!
  owner: String!
  description: String
  updatedAt: AWSDateTime
  createdAt: AWSDateTime
}
$ amplify mock apiを実行したままであれば、変更を検知して再読み込みしてくれます。
今回はコンパイルが通りました!こうした細かな間違いを$ amplify pushをせずに試せるのは嬉しいですね。
フロントエンドからGraphQLでクエリするためのコードを自動生成するため、いくつか聞かれます。全てデフォルトの選択肢で大丈夫です。
本来であればMock用のAPIエンドポイントが表示されるのですが、Error後に上記の設定を行うとAPIエンドポイントが表示されないようです。一度Ctrl+Cで実行を止め、再度$ amplify mock apiしましょう。
http://192.168.1.6:20002でGraphQLのMockサーバーが立ち上がりました!
192.168.1.6の部分は検証環境のネットワークでご自身のPC(またはサーバーなど)に割り当てられたローカルIPアドレスになります。
API Mockingで作成されるもの
API Mockingが動かせたところで、$ amplify mock apiした時に何が作られているのか確認して、全体感を抑えておきましょう。
New – Local Mocking and Testing with the Amplify CLI | AWS News Blogによれば、$ amplify mock apiによって以下のリソースが作成されます。
- 
the GraphQL transformations required by your API
 - 
DynamoDB Local to manage your API data locally
 - 
the Amplify GraphQL Explorer, based on the open source OneGraph graphiql-explorer plugin
 - 
GraphQL transformatinos
- 
schema.graphqlからGraphQLで使用されるより詳細なschema、resolver、およびフロントエンドで使用するコードを生成 
 - 
 - 
- SQLiteのDBをDynamoDB風のインタフェースでラップしたもので、Amplify GraphQL ExplorerのDBとして使用
 - SQLiteなのでSQLiteが読めるプラグインをいれて自由に閲覧・編集可能
 
 - 
Amplify GraphQL Explorer
- OSSのOneGraph graphiql-explorer pluginをベースに開発されたGraphiQL(GraphQL IDEとも)プラグイン
 - HTTPによるクエリを受け付けるGraphQLエンドポイントと、クエリを手軽に試せるGUIを提供
 
 
紹介されていませんが、地味に便利なのがaws-exportsをMockingの間だけlocalのMock API Endpointを指すように変更してくれる機能です。これについても後ほど確認します。
Amplify GraphQL Explorer
前項で立ち上がったエンドポイントにアクセスしてみましょう。
http://192.168.1.6:20002

Amplify GraphQL Explorerが立ち上がっています。いくつかアイテムを作成しましょう。
- 
- ADD NEW MUTATIONをクリック
 
 - createTodoをクリック
 - 紫文字のinputフィールドのうち、必須である
id, name, ownerを適当に埋めましょう(必須フィールドにはid*のように*がついています) - 返り値として欲しいフィールドにチェックを入れます(今回は全て)
 
このように、GraphiQLのサポートのもと、非常に簡単にmutationを書くことができました。それでは実際に上部の▶ をクリックして実行してみましょう。
右側に実行結果が表示されます。エラーがでました。メッセージを読むとcreateTodoにアクセスする権限がありませんと表示されます。
schema.graphqlで書いた内容を思い出してみましょう。
- Cognito User Poolで認証したユーザーに次のアクションを許可
- Todoのcreate
 - 自身が作成したアイテムのread, update, delete
 - 他の人が作成したアイテムのread
 
 - IAMで認証したユーザーと、AppSyncのAPI_KEYで認証したユーザーにはreadのみを許可
 
右上を見ると、Use: API Keyと表示されており、Amplify GraphQL Explorerがクエリを発行する際、認証情報としてAPI_KEYを使っていることがわかります。API_KEYで認証したユーザーにはread権限しか与えていないため弾かれたのですね。Amplify GraphQL Explorerがちゃんと@authを再現して認可をしていることがわかります。
では、create権限をもったCognito User Poolで認証したユーザーに切り替えてみましょう。
Use: API Keyをクリックし、Use: User Poolに切り替えます。
続いて隣のUpdate Authをクリックし、Auth Optionsを開きましょう。
UsernameとEmailに適当な値を入力し、Generate Tokenをクリックします。
UsernameはMutationのinputのownerと一致する必要があることに注意してください。
この状態で再度Mutationを実行してみましょう。
無事Mutationすることができました!
続けて別のユーザーに切り替えてみます。再度Update Authをクリックしてユーザーの情報を書き換えましょう。

Generate Token後、先ほど他のユーザー作ったTodoにMutation(update)をしてみます。
失敗しますね、設定通りです。
最後にAPI_KEY認証に変え、listTodoしてみましょう。
API_KEY認証でreadできることが確認できました。
ローカルのReactアプリからMockされたEndpointを叩く
Amplify CLIでは、 $ amplify add {category}して$ amplify pushすると、Amplify Framework(SDK)から作成したリソースを叩くために必要な情報をaws-exports.jsに吐き出してくれます。aws-exports.jsの中身を使ってAmplifyの初期設定を行うとリソース変更や追加をしてもコンフィグを書き換える必要がなくなり、非常に楽です。
例えばJavaScriptでは、次のようにAmplifyの設定を行います。
import awsmobile from './aws-exports';
import Amplify from 'aws-amplify';
Amplify.configure(awsmobile)
aws-exports.jsの中身をみてみましょう。
// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.
const awsmobile = {
    "aws_project_region": "us-west-2"
};
export default awsmobile;
今回は一度も$ amplify pushしていないため、リソースに関する記述が一切ありません。この状態で$ amplify mock apiするとどうなるのでしょうか。
$ amplify mock api
でAPI Mockingしたまま、aws-exports.jsを確認してみましょう。
// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.
const awsmobile = {
    "aws_project_region": "us-west-2",
    "aws_appsync_graphqlEndpoint": "http://10.221.93.66:20002/graphql",
    "aws_appsync_region": "us-west-2",
    "aws_appsync_authenticationType": "API_KEY",
    "aws_appsync_apiKey": "da2-fakeApiId123456",
    "aws_appsync_dangerously_connect_to_http_endpoint_for_testing": true
};
export default awsmobile;
API Mockingをしている間だけ、ローカルのGraphQL Endpointを指すようにaws-exports.jsを書き換えてくれるのです!便利!
ちなみにここではAPI_KEY認証を使っていますが、前述のAmplify GraphQL ExplorerのUpdate Authを行ってUse: User Poolにしてもaws-exports.jsには反映されないようです。。。updateに期待しましょう。
ではAPI MockingをCtrl+Cで抜けてaws-exports.jsを確認してみます。
// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.
const awsmobile = {
    "aws_project_region": "us-west-2"
};
export default awsmobile;
空っぽの状態に戻っていますね。
API Mocking Tips
API Mockingのちょっとした豆知識を紹介します。
API Mockingに対応しているディレクティブ
https://aws-amplify.github.io/docs/cli-toolchain/usage?sdk=js#api-mocking-setup によれば、$ amplify mock apiに対応しているディレクティブは執筆時点で次の5つです。@searchableと@predictionだけ対応していません。
- @auth
- @key
- @connection
- @versioned
- @function
DynamoDB Localに破壊的な変更を加える
注)以下で紹介するテクニックは裏技的なもので、今後アップデートにより必要なくなったり、そもそも別の方法があったりするかもしれないものです。(他に方法があればぜひ教えてください!)最新情報はドキュメントやGitHubの確認をお願いします。
開発していると、DynamoDBにPK,SKの変更を伴う破壊的な変更をしたくなる場合があります。本番環境であればGSI(グローバルセカンダリインデックス)の追加や、メンテナンスウィンドウを設けてのTable移行になるかと思います。重ねての紹介で恐縮ですが、AmplifyでGSIを使ってみようという内容も前回のAmplify Advent Calendar記事で書いたのでよければご覧ください。
突如idでなくnameフィールドでクエリしたい衝動に駆られました。schema.graphqlを次のように変更しましょう。
type Todo 
@model 
@key(fields: ["name"])
@auth(rules: [
  {allow: owner, provider: userPools, operations: [create, read, update, delete]},
  {allow: private, provider: userPools, operations: [read]},
  {allow: public, provider: iam, operations: [read]},
  {allow: public, provider: apiKey,operations: [read]},
])
{
  id: ID!
  name: String!
  owner: String!
  description: String
  updatedAt: AWSDateTime
  createdAt: AWSDateTime
}
$ amplify mock apiのログを見ると、普通に変更できていそうです。
GraphQL schema compiled successfully.
...(中略)...
Creating table DataStore locally
Creating table TodoTable locally
Running GraphQL codegen
✔ Generated GraphQL operations successfully and saved at src/graphql
Amplify GraphQL Exploererはホットリロードされないので、一度リロードしてからMutationを実行してみましょう。
Use User Poolになっていることを確認してください。
DynamoDB:ValidationExceptionが発生しました。作成時のPK, SKのままのDynamoDB LocalにPK変換後のMutationを投げてしまったためです。(ちなみに$ amplify pushの場合、DynamoDBのPK,SKの更新を伴う変更は、CloudFormation実行時に弾かれます。)
こうした場合、クラウドリソースであれば一度変更したいtypeをまるっとコメントアウトして$ amplify pushしてDynamoDB Tableを削除し、その後コメントアウトを解除して$ amplify pushすることで再度作成するというテクニックがあります。API Mockingの場合にはSQLiteのファイルを削除するとよさそうです。
$ rm amplify/mock-data/dynamodb/fake_us-fake-1.db
$ amplify mock api
GraphQL schema compiled successfully.
Creating table DataStore locally
Creating table TodoTable locally
Running GraphQL codegen
✔ Generated GraphQL operations successfully and saved at src/graphql
AppSync Mock endpoint is running at http://10.221.93.17:20002
ログから、TodoTableが作成されたことがわかります。Amplify GraphQL Explorerを更新して、再度Mutationを実行してみましょう。
続いてQueryを実行します。
無事、新しいPKであるnameフィールドを使ってgetTodoできました。
まとめ
- Mockingにより、クラウドリソースに変更反映する前に、変更後の動作確認をローカル環境で行うことが可能になります
 - Mockingが提供されているCategoryはAPI(GraphQL)、Storage、Functionの三つ
 - API Mockingが提供するのは次の四つです
- GraphQL transformations
 - DynamoDB Local
 - Amplify GraphQL Explorer
 - aws-exports.jsの自動編集
 
 
Amplify Mockingによって開発スピードが大幅に向上した実感があります。細かい変更確認のために$ amplify pushしていたあの頃には戻れそうにありません。
ぜひAPI Mockingだけでなく、Storage、FunctionのMockingについても触ってみてください!
AWS Amplify Advent Calendar、明日は @rtaguchi さんです!













