AWS AmplifyとVue.jsでWebアプリ(GraphQL形式のAPI)を速攻で作る。

IT記事

AWS Amplify触ってみる。GraphQL形式のapiとそれを使ったVueアプリがサラッと作れる。

今回は公式ドキュメントのチュートリアルを試す。

まずはvue cliでプロジェクトを作る。

vue create amplifytest

次にamplify cliのインストールと認証設定。

npm install -g @aws-amplify/cli
amplify configure

configureすると、ログインとか、IAMでルートユーザーを作らされる。途中でブラウザが開くので操作をして完了したら、コマンドに戻って進める。

次にvue cliで作ったプロジェクトに移動

cd amplifytest

ここからamplicy cliを使って、設定をしていく。まずプロジェクト全体のAWS Amplifyについての設定をする。

amplify init

プロジェクトのことを聞かれる。

Scanning for plugins...
Plugin scan successful
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project amplifytest
? Enter a name for the environment test
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using vue
? Source Directory Path:  src
? Distribution Directory Path: dist
? Build Command:  npm.cmd run-script build
? Start Command: npm.cmd run-script serve
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 (Use arrow keys)
> default (node:9952) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 keypress listeners add
? Please choose the profile you want to use default

プロジェクトの名前とか、vueを使いますよとか設定していく。

API(Amplify)を使う。

次に、Amplifyで使う機能(API)の設定をしていく。

ここで使うのはAPIというのはモデル定義ファイルを書くだけで、バックエンドapiと、クライアントコードを作ってくれる。

以下のコマンドを打つ。

amplify add api
? Please select from one of the below mentioned services: GraphQL
? Provide API name: amplifytest
? Choose the default authorization type for the API API key
? Enter a description for the API key: amplifytest
? After how many days from now the API key should expire (1-365): 7
? Do you want to configure advanced settings for the GraphQL API No, I am done.
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? Yes
? What best describes your project: (Use arrow keys)
> Single object with fields (e.g., “Todo” with ID, name, description)
? What best describes your project: Single object with fields (e.g., “Todo” with ID, name, description)
? Do you want to edit the schema now? No

クエリ形式にGraphQLを選び、認証方法をとりあえず7日間使えるAPI keyを選んだ。後からも変えられるようだ。

GraphQLスキーマ定義言語(SDL)で書かれたモデル定義ファイルというのがある。 今回は最初から用意されているTodoスキーマ定義をそのまま使う。

するとAPIの設定と、 Todoスキーマ定義 のファイルが作られる。定義ファイルは amplify/backend/api/YOUR-API-NAME/schema/  フォルダに.schemaという拡張子 でできている。

設定をして、定義ファイルを用意したので、今度は定義にしたがってawsにapiを作ってもらう。

amplify push
? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src\graphql\**\*.js
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2

スキーマ定義からクライアント用のGraphQLコードを生成するかい?上書きするよ?といったことを聞かれる。yes.

すると色々やってくれる。バックエンド側は、AWSのAmazon DynamoDBと、AWS AppSyncというサービスで作られている。以下のコマンドでコンソールが開く。

amplify console api

クライアント用のコードも生成されている。基本的なCRUD操作のGraphQLクエリとtypescript型定義が src/graphql フォルダに生成されている。

Vue.jsからAmplifyする

よしバックエンド側はこれでできたので、次はVue.jsだ。まずVue用のAmplifyコンポーネントをインストールする。最近のサービスはこういうのも用意してくれているんだ。

npm i aws-amplify
npm i aws-amplify-vue

読み込むコードを書く。よくわからないからコピペだ。

import Vue from "vue";
import App from "./App.vue";
import router from "./router";

Vue.config.productionTip = false;

// aws amplify
import Amplify, * as AmplifyModules from "aws-amplify";
import { AmplifyPlugin } from "aws-amplify-vue";
import awsconfig from "./aws-exports";
Amplify.configure(awsconfig);
Vue.use(AmplifyPlugin, AmplifyModules);

new Vue({
  router,
  render: h => h(App)
}).$mount("#app");

ここからAWS AmplifyのドキュメントのVueのページにあるコードを書いてみる。

今回作るのは、Todoを作成したら、画面に反映する。つまりcreate操作とsubscription操作を試す。 もしかすると複数ユーザーでも反映される。

説明は省くが以下のようになる。

<template>
  <div class="home">
    <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">Error!</div>

        <div v-else-if="data">
          <ul>
            <li v-for="item in data.listTodos.items" v-bind:key="item.id">
              <span>{{item.id}} - {{item.name}} - {{item.description}}</span>
            </li>
          </ul>
        </div>
      </template>
    </amplify-connect>

    <amplify-connect :mutation="createTodoMutation" @done="onCreateFinished">
      <template slot-scope="{ loading, mutate, errors }">
        <div v-if="loading">Loading...</div>
        <div v-else-if="errors.length > 0">Error!</div>
        <input v-model="form.name" placeholder="item name" />
        <input v-model="form.description" placeholder="item description" />
        <button :disabled="loading" @click="mutate">Create Todo</button>
      </template>
    </amplify-connect>
  </div>
</template>

<script>
const ListTodosQuery = `query ListTodos {
    listTodos {
      items {
        id
        name
        description
      }
    }
  }`;

const OnCreateTodoSubscription = `subscription OnCreateTodo {
      onCreateTodo {
        id
        name
        description
      }
    }`;

const CreateTodoMutation = `mutation CreateTodo($name: String!, $description: String) {
    createTodo(input: { name: $name, description: $description }) {
      id
      name
      description
    }
  }`;

export default {
  name: "Home",

  data() {
    return {
      form: { name: "", description: "" }
    };
  },

  computed: {
    listTodosQuery() {
      return this.$Amplify.graphqlOperation(ListTodosQuery);
    },
    createTodoSubscription() {
      return this.$Amplify.graphqlOperation(OnCreateTodoSubscription);
    },
    createTodoMutation() {
      return this.$Amplify.graphqlOperation(CreateTodoMutation, {
        name: this.form.name,
        description: this.form.description
      });
    }
  },
  methods: {
    onCreateTodo(prevData, newData) {
      console.log("New todo from subscription...");
      const newTodo = newData.onCreateTodo;
      prevData.data.listTodos.items.push(newTodo);
      return prevData.data;
    },
    onCreateFinished() {
      console.log("Todo created!");
    }
  }
};
</script>

npm run serveで確認できる。

というわけでできた。動くとテンション上がる。

スキーマ定義をしていけば、作りたいアプリのプロトタイプをどんどん作っていけそうなので、楽しみ。