AWS AppSyncで始めるフルマネージドのGraphQL

皆さん、GraphQLのサーバーサイドはどうやって実装されていますか？色々なライブラリがありますが、自前でサーバーを立ててセットアップするのは結構大変ですよね。
そこでAWSのGraphQLのフルマネージドサービス **AWS AppSync** をオススメしたいと思います。

# AWS AppSyncとは
AWS AppSyncは、GraphQLをベースとしたアプリケーションのバックエンドを提供するAWSのフルマネージドサービスです。次の機能を利用できます。

- リアルタイムアプリケーションの作成
- 同期を使ったオフラインプログラミングモデルの構築
- 必要なデータだけ GraphQL で取得
- 複数のデータソース（Amazon DynamoDB、Amazon Elasticsearch Service、AWS Lambda、Aurora Serverless）へのアクセス

詳細はクラスメソッドさんの記事が分かりやすいです。
[【速報】マネージドGraphQLサービス「AWS AppSync」が一般公開（GA）されました！ ｜ DevelopersIO](https://dev.classmethod.jp/cloud/aws/app-sync-ga/)

# AWS AppSyncのクイックスタート
AWS公式のクイックスタートを試してみました。
https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/quickstart.html

AWSアカウントさえあれば簡単にできるので、AppSyncの概要を掴むにはとりあえずやってみるのが早いと思います。
~~ただ、2018/11/26時点でドキュメントは英語版のみです。英語を読むのが億劫な方や、結果だけ知りたいという方は本記事を斜め読みしてみてください。~~
2020/05/06時点 日本語ドキュメントにも対応しています。

## リソースの作成
まずはAWSのマネジメントコンソールにアクセスし [AppSync] を選択します。リージョンはお好みで。AppSyncの画面で [Create API] をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/6574024d-ef71-b12c-5600-a61cb8c82804.png)

デフォルトのまま [Start] をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/65c64f70-9f26-7958-898b-790a1da6daab.png)

デフォルトのまま [Create] をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/72358476-295a-c756-8a6e-9f878ba6326a.png)

デフォルトのまま [Create] をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/12d48a05-7441-c990-3204-29cb27b5434c.png)

進行状況が表示されます。このまま10秒程度待ちます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/9706566f-2b5f-e2a7-baa7-69ac4a4732db.png)

GraphQL APIが作成されました！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/ec94789a-cad5-b442-549d-ccad5011da32.png)

## 作成されたリソースの確認
上記の操作だけで、以下のAWSリソースが作成されます。

リソース | リソース名 | 説明
--- | --- | ---
GraphQL API | `My AppSync App` | AppSyncにおける最上位のリソースです。1つのAPIに複数のスキーマやクエリが紐付きます。
DynamoDBテーブル | `MyModelTypeTable` | GraphQL APIが利用するDynamoDBテーブルです。

それでは細かく見ていきたいと思います。

### Queries: クエリとミューテーションの管理
Queriesを見てみると画面上部にミューテーションとクエリが1つずつ表示されています。コメントの訳は以下の通りです。

- **mutation createMyModelType**
    - DynamoDBにオブジェクトを作成するためにオレンジの`Play`ボタンをクリックして`createMyModelType`mutationを選択してください。開始して`Unable to assume role`というエラーが表示された場合は少し待ってから再度トライして見てください。
- **query listMyModelTypes**
    - `createMyModelType`を実行後、`listMyModelTypes`を実行してください。

```graphql:mutation-and-query
# Click the orange "Play" button and select the createMyModelType
# mutation to create an object in DynamoDB.
# If you see an error that starts with "Unable to assume role",
# wait a moment and try again.
mutation createMyModelType($createmymodeltypeinput: CreateMyModelTypeInput!) {
  createMyModelType(input: $createmymodeltypeinput) {
    id
    title
  }
}

# After running createMyModelType, try running the listMyModelTypes query.
query listMyModelTypes {
  listMyModelTypes {
    items {
      id
      title
    }
  }
}
```

それでは指示に従って操作してみます。オレンジのボタンをクリックして `createMyModelTypes` をクリックしてみます。これにより画面下部の `QUERY VARIABLE` の値がDynamoDBテーブルに登録されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/49ff6861-abf8-9018-d7cd-b1bdebb4abb0.png)

```json:query-variable
{
  "createmymodeltypeinput": {
    "title": "Hello, world!"
  }
}
```

画面右側にクエリの実行結果が表示されます。`id`=ランダムっぽい値、`title`=`Hello, world!`のオブジェクトが表示されています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/b0fde8e7-8663-a4f7-9a36-77bdf8d82412.png)

```json:mutation-result
{
  "data": {
    "createMyModelType": {
      "id": "f3dcfbfe-d425-43dc-96d0-62b0feca3804",
      "title": "Hello, world!"
    }
  }
}
```

オレンジのボタンをクリックして `listMyModelTypes` をクリックしてみます。これによりDynamoDBテーブルへの問い合わせが行われます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/1f52986c-a807-3940-6b0f-adaa8695cbf6.png)

右側にクエリの実行結果が表示されます。`item`の配列に先ほど登録したオブジェクトが表示されました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/c7d24948-c126-e71a-2ef2-36508d187df6.png)

試しにDynamoDBのテーブルを見てみましょう。同じリージョンのDynamoDBコンソールにアクセスすると `MyModelTypeTable` というテーブルが出来ています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/1230e31a-fabe-eb9d-1b54-f401357a30d2.png)

テーブル名左側のラジオボタンをチェックし [項目] タブをクリックすると、テーブルに登録されたアイテムが確認できます。1件登録されていることが確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/aa5e1ca8-0b88-df1c-3a21-6b28011a12c1.png)

### Queries: ドキュメントの確認
画面右側の [Docs] をクリックしてみます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/3c412c4c-8d39-c04e-7111-b1a0fe083698.png)

Documentation Explorerが表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/cb7158ee-e362-3fef-fe3d-6713e663c3ce.png)

[Query] などをクリックすると、クエリの詳細が確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/1cbe8ab7-d7d2-f20a-87e7-7368b383fa7e.png)

### Schema: スキーマの管理
左ペインの [Schema] をクリックするとスキーマの一覧が確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/6f6e6d6f-0a3f-943a-1fa5-c5efb8559428.png)

以下は作成されたスキーマの一覧です。一つ一つの説明は省略しますが、ミューテーション、クエリ、サブスクリプションという機能を網羅したスキーマ構成になっています。

```graphql:schema
input CreateMyModelTypeInput {
	title: String
}

input DeleteMyModelTypeInput {
	id: ID!
}

type Mutation {
	createMyModelType(input: CreateMyModelTypeInput!): MyModelType
	updateMyModelType(input: UpdateMyModelTypeInput!): MyModelType
	deleteMyModelType(input: DeleteMyModelTypeInput!): MyModelType
}

type MyModelType {
	id: ID!
	title: String
}

type MyModelTypeConnection {
	items: [MyModelType]
	nextToken: String
}

type Query {
	getMyModelType(id: ID!): MyModelType
	listMyModelTypes(filter: TableMyModelTypeFilterInput, limit: Int, nextToken: String): MyModelTypeConnection
}

type Subscription {
	onCreateMyModelType(id: ID, title: String): MyModelType
		@aws_subscribe(mutations: ["createMyModelType"])
	onUpdateMyModelType(id: ID, title: String): MyModelType
		@aws_subscribe(mutations: ["updateMyModelType"])
	onDeleteMyModelType(id: ID, title: String): MyModelType
		@aws_subscribe(mutations: ["deleteMyModelType"])
}

input TableBooleanFilterInput {
	ne: Boolean
	eq: Boolean
}

input TableFloatFilterInput {
	ne: Float
	eq: Float
	le: Float
	lt: Float
	ge: Float
	gt: Float
	contains: Float
	notContains: Float
	between: [Float]
}

input TableIDFilterInput {
	ne: ID
	eq: ID
	le: ID
	lt: ID
	ge: ID
	gt: ID
	contains: ID
	notContains: ID
	between: [ID]
	beginsWith: ID
}

input TableIntFilterInput {
	ne: Int
	eq: Int
	le: Int
	lt: Int
	ge: Int
	gt: Int
	contains: Int
	notContains: Int
	between: [Int]
}

input TableMyModelTypeFilterInput {
	id: TableIDFilterInput
	title: TableStringFilterInput
}

input TableStringFilterInput {
	ne: String
	eq: String
	le: String
	lt: String
	ge: String
	gt: String
	contains: String
	notContains: String
	between: [String]
	beginsWith: String
}

input UpdateMyModelTypeInput {
	id: ID!
	title: String
}
```

### Data Sources: データソースの管理
左ペインの [Data Sources] をクリックするとデータソースの一覧が確認できます。[Resource] 列のリンクを押すとDynamoDBの画面に遷移します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/89a500cc-905a-4339-760e-dfb4294ea5bb.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/5aff5860-997d-302c-f292-67bc14fbf453.png)

### Settings: 設定の管理
左ペインの [Settings] をクリックするとGraphQL APIの設定が確認できます。クライアントからGraphQL APIにアクセスする際にこれらの情報を利用します（既に削除済なのでこのAPIにはアクセスできません）。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284925/3a0a1a43-0f47-c1ef-53e2-099daf7e5015.png)

## リソースの削除
放置すると微々たるものですが料金が発生するため、リソースを削除します。

- AppSync
    - 左ペインの [APIs] で対象のGraphQL APIを選択し [Delete] をクリックします
- DynamoDB
    - 左ペインの [テーブル] で対象のテーブルを選択肢 [Delete] をクリックします

クイックスタートをなぞっただけの内容でしたが、簡単にフルマネージドのGraphQL APIを作ることが出来ました。皆さんもぜひトライしてみてください！