はじめに
先日書いた記事の続きです。
Amplifyでは、GraphQLを使って、APIを追加する事ができます。
GraphQLとは
GraphQLを使うと従来のRESTfulなAPI設計とは違って、過不足なく必要なデータを取ってくる事ができます。JSONをやり取りする感覚に近く、とても直感的です。
Amplifyでは、GraphQLでデータの構造を定義するとそれに伴い
- データの追加
- データの更新
- データの取得
- データの削除
を行う関数を自動で作成してくれます。便利ですね。
セットアップ
コンソール上で次のコマンドを実行します。
$ amplify add api
指示にしたがって、設定を行います。
Authorization typeを設定していきます。
今回は、Amazon Cognito User Poolを選択しました。
各方式の特徴はこちらにまとめています。
type Todo @model {
id: ID!
name: String!
description: String
}
今回は、標準のTodoをそのまま使っていきます。
!マークがついた場合、nullを許さないという意味です。つまり必須項目ですね。
次に、GraphQLのテーブルをリモートで作成していきます。
amplify push
✔ Successfully pulled backend environment develop from the cloud.
Current Environment: develop
| Category | Resource name | Operation | Provider plugin |
| -------- | --------------------- | --------- | ----------------- |
| Api | amplifysample | Create | awscloudformation |
| Auth | amplifysample******** | No Change | awscloudformation |
作成の準備ができているようですね。
他の項目は全てデフォルトのままにして進めていきます。
クラウドへの反映に時間がかかりますが、そのまま待ちましょう。
作成が完了すると、GraphQLのエンドポイントが表示されるので、メモしておきましょう。
GraphQL endpoint: https://*******.appsync-api.us-east-1.amazonaws.com/graphql
このhttps://から始まるURLが、GraphQLの操作の起点になるので、メモしておきましょう。
続いて、GraphQLを操作するための関数が自動的に作成されます。早速確認していきましょう。
src/graphql
を見てみましょう。
まずはmutation.js
を確認します。
/* eslint-disable */
// this is an auto generated file. This will be overwritten
export const createTodo = `mutation CreateTodo(
$input: CreateTodoInput!
$condition: ModelTodoConditionInput
) {
createTodo(input: $input, condition: $condition) {
id
name
description
}
}
`;
export const updateTodo = `mutation UpdateTodo(
$input: UpdateTodoInput!
$condition: ModelTodoConditionInput
) {
updateTodo(input: $input, condition: $condition) {
id
name
description
}
}
`;
export const deleteTodo = `mutation DeleteTodo(
$input: DeleteTodoInput!
$condition: ModelTodoConditionInput
) {
deleteTodo(input: $input, condition: $condition) {
id
name
description
}
}
`;
ミューテーションとは、通常のデータベースに置ける、CREATE, UPDATE, DELETEの操作を指します。
GraphQLでは、データを変えうる処理をまとめてミューテーションと呼んでいるようですね。
続いて、queries.js
を確認して見ましょう。
/* eslint-disable */
// this is an auto generated file. This will be overwritten
export const getTodo = `query GetTodo($id: ID!) {
getTodo(id: $id) {
id
name
description
}
}
`;
export const listTodos = `query ListTodos(
$filter: ModelTodoFilterInput
$limit: Int
$nextToken: String
) {
listTodos(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
name
description
}
nextToken
}
}
`;
getTodoは、特定のTodoを取得するもので、listTodosは、存在するTodoをリスト形式でまとめて取得するものです。
さらに、subscription.js
を確認していきます。
/* eslint-disable */
// this is an auto generated file. This will be overwritten
export const onCreateTodo = `subscription OnCreateTodo {
onCreateTodo {
id
name
description
}
}
`;
export const onUpdateTodo = `subscription OnUpdateTodo {
onUpdateTodo {
id
name
description
}
}
`;
export const onDeleteTodo = `subscription OnDeleteTodo {
onDeleteTodo {
id
name
description
}
}
`;
AppSyncを使うことで、データの変更を監視する事ができます。新たなTodoが追加されたり、既存のTodoの変更・削除されたタイミングで、即座にデータを反映する事ができます。
Vueの編集
実際にTodoを追加できるように、Vueファイルを編集していきます。
まずは、views/Home.vue
を編集します。
<template>
<div class="home">
<new-todo></new-todo>
<amplify-connect :query="listTodosQuery"
:subscription="createTodoSubscription"
:onSubscriptionMsg="onCreateTodo">
<template slot-scope="{loading, data, errors}">
<div v-if="loading">Loading...</div>
<div v-else-if="errors.length > 0"></div>
<div v-else-if="data">
<TodoList :items="data.listTodos.items"></TodoList>
</div>
</template>
</amplify-connect>
</div>
</template>
<script>
import { components } from 'aws-amplify-vue';
import TodoList from '@/components/TodoList.vue';
import NewTodo from '@/components/NewTodo.vue';
const ListTodosQuery = `query ListTodos {
listTodos {
items {
id
name
}
}
}`;
const OnCreateTodoSubscription = `subscription OnCreateTodo {
onCreateTodo {
id
name
}
}`;
export default {
name: 'home',
components: {
NewTodo,
TodoList,
...components
},
computed: {
listTodosQuery() {
return this.$Amplify.graphqlOperation(ListTodosQuery);
},
createTodoSubscription() {
return this.$Amplify.graphqlOperation(OnCreateTodoSubscription);
}
},
methods: {
onCreateTodo(prevData, newData) {
console.log('New todo from subscription...');
const newTodo = newData.onCreateTodo;
prevData.data.listTodos.items.push(newTodo);
return prevData.data;
}
}
}
</script>
続いて、components/NewTodo.vue
を追加します。
<template>
<div>
<amplify-connect :mutation="createTodoMutation" @done="onCreateFinished">
<template slot-scope="{ loading, mutate, }">
<input v-model="name" placeholder="item name" />
<input v-model="description" placeholder="item description" />
<button :disabled="loading" @click="mutate">Create Todo</button>
</template>
</amplify-connect>
</div>
</template>
<script>
import { components } from 'aws-amplify-vue';
const CreateTodoMutation = `mutation CreateTodo($name: String!, $description: String) {
createTodo(input: { name: $name, description: $description }) {
id
name
}
}`;
export default {
name: 'NewTodo',
components: {
...components
},
data () {
return {
name: '',
description: ''
}
},
computed: {
createTodoMutation () {
return this.$Amplify.graphqlOperation(CreateTodoMutation,
{ name: this.name, description: this.description });
}
},
methods: {
onCreateFinished () {
console.log('Todo created!');
}
}
}
</script>
さらに、components/TodoList.vue
を編集していきます。
<template>
<div>
<ul v-for="item in items" :key="item.id">
<li>
{{item}}
</li>
</ul>
</div>
</template>
<script>
export default {
props:['items'],
}
</script>
解説
amplify-connectコンポーネントの実装はこちらから確認する事ができます。
クエリを読み込んで、そのまま実行している事がわかります。
実行
データ構造
では、GraphQLで追加したデータがどのように格納されているか確認して見ましょう。
AWSコンソールでDynamoDBにアクセスします。
テーブルにアクセスします。
固有のIDと、型名、作成時刻、各パラメータ、更新時刻が格納されています。IDがパーテーションキーになっているので、IDを使うと$O(1)$でデータを取得する事ができます。
ディレクティブ
これからはより複雑なスキーマを作っていきます。
@keyディレクティブ
@keyディレクティブを追加すると、keyに指定した属性で検索できるようになります。
今回はメールアドレスの一意性を用いてユーザを追加します。
まずはschema.graphqlを変更します。
type Todo @model {
id: ID!
name: String!
description: String
}
type User @model @key(fields: ["email"]) {
email: String!
username: String!
}
スキーマをローカルでただ変更しただけでは、実際に使用することはできません。
$ amplify push
を再度行って、ローカルのスキーマを更新します。
続いて、Userデータを更新するコンポーネントを作成します。
<template>
<div>
<amplify-connect :mutation="createUserMutation" @done="onCreateFinished">
<template slot-scope="{ loading, mutate, }">
<input v-model="email" placeholder="email" />
<input v-model="username" placeholder="username" />
<button :disabled="loading" @click="mutate">Create User</button>
</template>
</amplify-connect>
</div>
</template>
<script>
import { components } from 'aws-amplify-vue';
const CreateUserMutation = `mutation CreateUser($email: String!, $username: String!) {
createUser(input: { email: $email, username: $username }) {
email
username
}
}`;
export default {
name: 'NewUser',
components: {
...components
},
data () {
return {
email: '',
username: ''
}
},
computed: {
createUserMutation () {
return this.$Amplify.graphqlOperation(CreateUserMutation,
{ email: this.email, username: this.username });
}
},
methods: {
onCreateFinished () {
console.log('User created!');
}
}
}
</script>
続いて、メールアドレスを使って、ユーザ名をクエリする関数を作ります。
async getUser(){
const userInfo = await API.graphql(graphqlOperation(queries.getUser, { email: this.email }))
console.log(userInfo)
}
実際に実行すると、
{data: {…}}data: getUser: {email: "test@example.com", username: "John Doe"}__proto__: Object__proto__: Object
このように正しく先ほど追加したデータを見ることができます!
GraphQLクエリのAppSyncでの実行
今回NewUser.vueのなかで作成したmutationをもう一度見てみましょう。
mutation CreateUser($email: String!, $username: String!) {
createUser(input: { email: $email, username: $username }) {
email
username
}
}
CreateUserという関数(外側)の中で、$email
, $username
という変数を定義して、その中で
createUser(内側)を実行しています。
続いて、AppSync上でこのmutationを実行してみましょう。
まずはAWSコンソールにログインして、AppSyncを開きます。
「クエリを実行」をクリック
「ユーザープールでログイン」をクリック。
これはAPIの認証形式によりますので、設定中の形式でログインしてください。
Cognitoの場合の例を示します。
パラメータ | 入力内容 |
---|---|
ClientId | aws-exports.jsのaws_user_pools_web_client_id |
ユーザー名 | ログインできる有効なユーザのメールアドレス |
パスワード | ログインできる有効なユーザのパスワード |
続いて、実際にmutationを実行してみましょう。
先ほどは$email
のように$
を用いて変数を表していましたが、今回は直打ちで実行します。
mutation CreateUser {
createUser(input: { email: "test2@example.com" username: "Jane Doe"}) {
email
username
}
}
クエリを入力して実行すると、
このように登録完了を示すレスポンスが返ります。
DynamoDBでみてみると、
実際に追加されていることがわかります。
好評であれば次回に続く