LoginSignup
14

More than 3 years have passed since last update.

AWS AmplifyをVue.jsで使い倒したい!(API編)

Last updated at Posted at 2020-01-29

はじめに

先日書いた記事の続きです。

Amplifyでは、GraphQLを使って、APIを追加する事ができます。

GraphQLとは

GraphQLを使うと従来のRESTfulなAPI設計とは違って、過不足なく必要なデータを取ってくる事ができます。JSONをやり取りする感覚に近く、とても直感的です。

Amplifyでは、GraphQLでデータの構造を定義するとそれに伴い

  • データの追加
  • データの更新
  • データの取得
  • データの削除

を行う関数を自動で作成してくれます。便利ですね。

セットアップ

コンソール上で次のコマンドを実行します。

$ amplify add api

指示にしたがって、設定を行います。

スクリーンショット 2020-01-28 23.15.42.png

Authorization typeを設定していきます。
今回は、Amazon Cognito User Poolを選択しました。

各方式の特徴はこちらにまとめています。

schema.graphql
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を確認します。

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を確認して見ましょう。

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を確認していきます。

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を編集します。

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を追加します。

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を編集していきます。

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コンポーネントの実装はこちらから確認する事ができます。
クエリを読み込んで、そのまま実行している事がわかります。

実行

スクリーンショット 2020-01-29 18.20.03.png
新たなTodoが追加されているのが確認できます。

データ構造

では、GraphQLで追加したデータがどのように格納されているか確認して見ましょう。
AWSコンソールでDynamoDBにアクセスします。
スクリーンショット 2020-01-29 18.23.45.png
テーブルにアクセスします。
スクリーンショット 2020-01-29 18.26.15.png

固有のIDと、型名、作成時刻、各パラメータ、更新時刻が格納されています。IDがパーテーションキーになっているので、IDを使うと$O(1)$でデータを取得する事ができます。

ディレクティブ

これからはより複雑なスキーマを作っていきます。

@keyディレクティブ

@keyディレクティブを追加すると、keyに指定した属性で検索できるようになります。
今回はメールアドレスの一意性を用いてユーザを追加します。
まずはschema.graphqlを変更します。

schema.graphql
type Todo @model {
  id: ID!
  name: String!
  description: String
}

type User @model @key(fields: ["email"]) {
  email: String!
  username: String!
}

スキーマをローカルでただ変更しただけでは、実際に使用することはできません。

$ amplify push

を再度行って、ローカルのスキーマを更新します。

続いて、Userデータを更新するコンポーネントを作成します。

NewUser.vue
<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>

続いて、メールアドレスを使って、ユーザ名をクエリする関数を作ります。

Home.vue
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
mutation CreateUser($email: String!, $username: String!) {
    createUser(input: { email: $email, username: $username }) {
      email
      username
    }
  }

CreateUserという関数(外側)の中で、$email, $usernameという変数を定義して、その中で
createUser(内側)を実行しています。

続いて、AppSync上でこのmutationを実行してみましょう。
まずはAWSコンソールにログインして、AppSyncを開きます。
スクリーンショット 2020-03-01 16.35.17.png
「クエリを実行」をクリック
スクリーンショット 2020-03-01 16.37.35.png
「ユーザープールでログイン」をクリック。
これは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
    }
  }

クエリを入力して実行すると、
スクリーンショット 2020-03-01 17.03.02.png
このように登録完了を示すレスポンスが返ります。

DynamoDBでみてみると、
スクリーンショット 2020-03-01 17.02.41.png
実際に追加されていることがわかります。

好評であれば次回に続く

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14