雑食日誌

Vue.jsとServerless。ときどきチーム開発

VueにAmplifyを入れてみた

この記事はVue.js #3 Advent Calendar 2018の4日目の記事です。
フロントエンドのライブラリかつAWSのリソースを作ることができるツールとして話題のAmplifyをVuejsに組み込んでみたいと思います。

Amplifyとは

AmplifyはAWSリソースとの接続を支援するオープンソースJavaScriptライブラリです。
強力なCLIツールが提供されていて、AWSリソースの作成をコマンド上で実行し、ライブラリに反映することができます。 現在はCognitoによる認証や、AppSyncを用いたGraphQLの利用がサポートされています。

The foundation for your cloud-powered mobile & web apps

CLIのインストール

インストールはNPMで行います。

$ npm i -g @aws-amplify/cli

下記コマンドで詳細が出力されたらインストール成功です。

$ amplify

Vueプロジェクトの初期化

Amplifyの初期化はプロジェクトのルートディレクトで実行することを推奨されているため、先にVueのプロジェクトを作成します。 実行はVue CLIを用いています。

keinuma% vue create amplify-vue-demo 

Vue CLI v3.1.3
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, Linter
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript for auto-detected polyfills? Yes
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: ESLint
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In package.json
? Save this as a preset for future projects? No

Amplifyの初期化

Amplify CLIを用いてAWS環境の初期化を行います。

$ amplify init

実行すると開発環境およびプロジェクトの情報を渡してあげます。

? 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:  yarn build
? Start Command: yarn serve

Amplifyは現状CloudFormationから各リソースを作成するため、認証情報を教えないといけません。
AWS上でユーザーを作成するか聞かれるので、専用のユーザーを用意する場合はYesを選択しましょう。
リージョンとアカウント名を入力するとIAM作成画面に遷移し、ポリシーの設定を行います。

Using default provider awscloudformation
AWS access credentials can not be detected.
? Setup new user Yes
Follow these steps to set up access to your AWS account:

Sign in to your AWS administrator account:
https://console.aws.amazon.com/
Press Enter to continue

Specify the AWS Region
? region:  ap-northeast-1
Specify the username of the new IAM user:
? user name:  amplify-demo
Complete the user creation using the AWS console
https://console.aws.amazon.com/iam/home?****

ユーザーを作成したら、コンソールに戻ってクレデンシャル情報を入力します。
Access Key IDおよびSecret Access Keyを入力したら、Amplify上で管理するための名前をつけることができます。

Press Enter to continue
Enter the access key of the newly created user:
? accessKeyId:  A****************
? secretAccessKey:  *******************************
This would update/create the AWS Profile in your local machine
? Profile Name:  demo

Successfully set up the new user.

認証情報の設定が完了すると改めて、ユーザー選択を行うので先ほど作ったdemoを選択します。

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use demo
⠇ Initializing project in the cloud...

CREATE_IN_PROGRESS amplifyvuedemo-20181126103438 AWS::CloudFormation::Stack Mon Nov 26 2018 10:34:38 GMT+0900 (日本標準時) User Initiated
CREATE_IN_PROGRESS UnauthRole                    AWS::IAM::Role             Mon Nov 26 2018 10:34:42 GMT+0900 (日本標準時)
CREATE_IN_PROGRESS DeploymentBucket              AWS::S3::Bucket            Mon Nov 26 2018 10:34:42 GMT+0900 (日本標準時)
CREATE_IN_PROGRESS AuthRole                      AWS::IAM::Role             Mon Nov 26 2018 10:34:42 GMT+0900 (日本標準時)
CREATE_IN_PROGRESS AuthRole                      AWS::IAM::Role             Mon Nov 26 2018 10:34:43 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS UnauthRole                    AWS::IAM::Role             Mon Nov 26 2018 10:34:43 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS DeploymentBucket              AWS::S3::Bucket            Mon Nov 26 2018 10:34:44 GMT+0900 (日本標準時) Resource creation Initiated
⠹ Initializing project in the cloud...

CREATE_COMPLETE UnauthRole       AWS::IAM::Role  Mon Nov 26 2018 10:35:02 GMT+0900 (日本標準時)
CREATE_COMPLETE AuthRole         AWS::IAM::Role  Mon Nov 26 2018 10:35:02 GMT+0900 (日本標準時)
CREATE_COMPLETE DeploymentBucket AWS::S3::Bucket Mon Nov 26 2018 10:35:05 GMT+0900 (日本標準時)
⠸ Initializing project in the cloud...

CREATE_COMPLETE amplifyvuedemo::AWS::CloudFormation::Stack Mon Nov 26 2018 10:35:09 GMT+0900 (日本標準時)
✔ Successfully created initial AWS cloud resources for deployments.

Your project has been successfully initialized and connected to the cloud!

Some next steps:
"amplify status" will show you what you've added already and if it's locally configured or deployed
"amplify <category> add" will allow you to add features like user login or a backend API
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud

Pro tip:
Try "amplify add api" to create a backend API and then "amplify publish" t

CloudFormationのStack、デプロイ用のバケットが作成されると初期化は完了になります。
実行完了後に以下のファイルが作成されています。

  • .amplifyrc
  • amplify/

.amplifyrcはCloudFormationで作成されたリソース情報が記載されていて、amplifyディレクトリ以下にはプロジェクトの情報やリソースの詳細が格納されています。

Cognitoの作成

AmplifyでCognitoユーザープールを作成してみたいと思います。
addコマンドでユーザープールの設定を行います。 認証方法はEmailで、パスワードの強度などをカスタマイズしています。 IdeintityプールはCognitoからAPI以外の他のAWSリソースに紐づけるときに使用します。
例えばS3へのアップロードを許可する場合などですね。

$ amplify add auth
Using service: Cognito, provided by: awscloudformation
 The current configured provider is Amazon Cognito.
 Do you want to use the default authentication and security configuration? No, I will set up my own configuration.
? Select the authentication/authorization services that you want to use: User Sign-Up, Sign-In, connected with AWS IAM controls (Enables per-user Stor
age features for images or other content, Analytics, and more)
? Please provide a friendly name for your resource that will be used to label this category in the project: amplifyauthdemo
? Please enter a name for your identity pool. amplifyvuedemo96530290_identitypool_96530290
? Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM) No
? Do you want to enable 3rd party authentication providers in your identity pool? No
? Please provide a name for your user pool: amplifyvuedemo96530290_userpool_96530290
? Multifactor authentication (MFA) user login options: OFF
? Email based user registration/forgot password: Enabled (Requires per-user email entry at registration)
? Please specify an email verification subject: パスワード再設定
? Please specify an email verification message: 認証コードはこちらになります。 {####}
? Do you want to override the default password policy for this User Pool? Yes
? Enter the minimum password length for this User Pool: 8
? Select the password character requirements for your userpool: Requires Lowercase, Requires Uppercase, Requires Numbers
? Userpool users are created with a standard set of attributes defined by the OpenID Connect specification.  Mark the required attributes below:
? Specify the app's refresh token expiration period (in days): 30
? Do you want to specify the user attributes this app can read and write? Yes
? Specify read attributes: Email
? Specify write attributes:
Successfully added resource amplifyauthdemo locally

Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud

作成したCognitoをAWSに反映してみる。 init時に作成したCloudFormationにネストしてリソースを作成していることがわかります。

$ amplify push
| Category | Resource name   | Operation | Provider plugin   |
| -------- | --------------- | --------- | ----------------- |
| Auth     | amplifyauthdemo | Create    | awscloudformation |
? Are you sure you want to continue? true
⠙ Updating resources in the cloud. This may take a few minutes...

UPDATE_IN_PROGRESS amplifyvuedemo-20181126103438 AWS::CloudFormation::Stack Mon Nov 26 2018 11:16:29 GMT+0900 (日本標準時) User Initiated
CREATE_IN_PROGRESS authamplifyauthdemo           AWS::CloudFormation::Stack Mon Nov 26 2018 11:16:34 GMT+0900 (日本標準時)
CREATE_IN_PROGRESS authamplifyauthdemo           AWS::CloudFormation::Stack Mon Nov 26 2018 11:16:36 GMT+0900 (日本標準時) Resource creation Initiated
⠼ Updating resources in the cloud. This may take a few minutes...
...
✔ All resources are updated in the cloud

完了するとCognitoが作成されていることを確認できます。
また、パスワードの設定などがカスマイズされていることも確認できます。

プロジェクトの src/aws-exports.js にユーザープールのIDが保存されます。

AppSyncの作成

続けてAppSyncを作成してみます。
認証方法は先ほど作成したCognitoを用いたいと思います。

$ amplify add api
? Please select from one of the below mentioned services GraphQL
? Provide API name: amplifyvuedemo
? Choose an authorization type for the API Amazon Cognito User Pool
Use a Cognito user pool configured as a part of this project
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? true
? What best describes your project:
? What best describes your project: One-to-many relationship (e.g., “Blogs” with “Posts” and “Comments”)
? Do you want to edit the schema now? No
? Press enter to continue

GraphQL schema compiled successfully. Edit your schema at ***/amplify-vue-demo/amplify/backend/api/amplifyvuedemo/schema.graphql
Successfully added resource amplifyvuedemo locally

Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud

今回はSchemaを定義せずにチュートリアルで用意されているブログ用のスキーマを選択しました。
チュートリアルではデータソースにDynamoDBが使われているので、AppSyncと直接同期することができます。
完了するとamplify/backend/api/{API name}/build/schema.graphqlが作成されて、スキーマが定義されます。

schema.graphql

type Blog @model {
  id: ID!
  name: String!
  posts: [Post] @connection(name: "BlogPosts")
}
type Post @model {
  id: ID!
  title: String!
  blog: Blog @connection(name: "BlogPosts")
  comments: [Comment] @connection(name: "PostComments")
}
type Comment @model {
  id: ID!
  content: String
  post: Post @connection(name: "PostComments")
}

Resolverが amplify/backend/api/{API name}/build/resolver 以下に格納され、リソースの詳細情報が保存されています。 作成したAppSyncをAWSに反映しましょう。

$ amplify push
| Category | Resource name   | Operation | Provider plugin   |
| -------- | --------------- | --------- | ----------------- |
| Api      | amplifyvuedemo  | Create    | awscloudformation |
| Auth     | amplifyauthdemo | No Change | awscloudformation |
? Are you sure you want to continue? true

GraphQL schema compiled successfully. Edit your schema at ***/amplify-vue-demo/amplify/backend/api/amplifyvuedemo/schema.graphql
? Do you want to generate code for your newly created GraphQL API (Y/n)
? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions (src/graphql/**/*.js)
? 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 (Y/n)
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes

⠇ Updating resources in the cloud. This may take a few minutes...

UPDATE_IN_PROGRESS amplifyvuedemo-20181126103438 AWS::CloudFormation::Stack Mon Nov 26 2018 11:34:52 GMT+0900 (日本標準時) User Initiated
UPDATE_IN_PROGRESS authamplifyauthdemo           AWS::CloudFormation::Stack Mon Nov 26 2018 11:34:57 GMT+0900 (日本標準時)
...

✔ Generated GraphQL operations successfully and saved at src/graphql
✔ All resources are updated in the cloud

GraphQL endpoint: https://***.appsync-api.ap-northeast-1.amazonaws.com/graphql

言語を選択してコード生成ができます。今回はJavaScriptを選択しています。

完了するとプロジェクトに以下のファイルが生成されています。

  • src/graphql
  • .graphqlconfig.yml

.graphqlconfig.ymlはAppSyncの情報が格納されています。
src/graphql は定義されているクエリやミューテーションが文字列として格納されている。 使用するときにクエリを選ぶことができる。

Vueパッケージをインストール

Vueのライブラリとして使用するためのパッケージをインストールする

$ npm i aws-amplify aws-amplify-vue

これでAmplifyをライブラリとして使う準備ができました。

ユーザーの認証画面を作成する

AmplifyにはいくつかUIが用意されています。
認証では Authenticator コンポーネントを組み込むことで認証フローの実装をスキップできます。

認証情報はストア上で行うため、先にストアの設定を行います。

store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    user: null
  },
  mutations: {
    setUser(state, user) {
      state.user = user
    }
  },
  actions: {
  }
})

認証情報はページ遷移ごとにチェックする必要があるため、ルーティングに認証チェックフローを実装します。
Vue Routerと一緒にAmplifyのライブラリのインポートを行います。

router.js

import Vue from 'vue'
import Router from 'vue-router'
import Home from '../views/Home.vue'
import AmplifyStore from '../store'
import { AmplifyEventBus, AmplifyPlugin, components } from 'aws-amplify-vue'
import * as AmplifyModules from 'aws-amplify'

Vue.use(Router)
Vue.use(AmplifyPlugin, AmplifyModules)

認証状態がパスワードチェック前なのかサインイン前なのかなどをEventBusで確認します。

router.js

let user;

getUser().then((user) => {
  if (user) {
    router.push({path: '/'})
  }
})

AmplifyEventBus.$on('authState', async (state) => {
  if (state === 'signedOut'){
    user = null;
    AmplifyStore.commit('setUser', null);
    router.push({path: '/auth'})
  } else if (state === 'signedIn') {
    user = await getUser();
    router.push({path: '/'})
  }
});

function getUser() {
  return Vue.prototype.$Amplify.Auth.currentAuthenticatedUser().then((data) => {
    if (data && data.signInUserSession) {
      AmplifyStore.commit('setUser', data);
      return data;
    }
  }).catch(() => {
    AmplifyStore.commit('setUser', null);
    return null
  });
}

ルーティング前にユーザーが設定されているかで、ログイン画面にルーティングするかを決める処理を実装します。

router.beforeResolve(async (to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    user = await getUser();
    if (!user) {
      return next({
        path: '/auth',
        query: {
          redirect: to.fullPath,
        }
      });
    }
    return next()
  }
  return next()
})

Amplifyが用意しているAuthenticatorコンポーネント/auth に宣言します。

const router = new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/auth',
      name: 'auth',
      component: components.Authenticator
    }
  ]
})

ローカル上で起動し http://localhost:8080/auth にアクセスするとログイン画面にルーティングされます。

f:id:keinumata:20181203235853p:plain

これで認証機能を作成することができました。
Authenticator コンポーネントはストアのユーザー情報とCogntioにリクエストした認証状態を確認し、適切なルーティングを行ってくれます。

GraphQLにリクエストしてみる

AppSyncのリソースと同時に作成された src/graphql/ を使うことでリクエストを簡単に実装できます。 はじめに必要なライブラリをインポートしましょう。

import { API, graphqlOperation } from 'aws-amplify'
import * as mutaions from '@/graphql/mutations'

メソッドにGraphQLへのリクエストを持ったコンポーネントを実装します。

export default {
  name: 'CreatePost',
  data: function() {
    return {
      postData: {
        author: '',
        content: ''
      },
      myPost: ''
    }
  },
  methods: {
    createPost: function () {
      API.graphql(graphqlOperation(mutaions.createPost, this.postData ))
        .then((response) => {
          console.log(response)
          this.myPost = response
        })
        .catch((err) => {
          console.log(err)
        })
    }
  }
}

graphqlOperation に使用するクエリとパラメータを指定したオブジェクトをAPI.graphql 関数に渡しています。 クエリを変更するだけで、リクエストを変えることができます。

実装の全体はGitHubリポジトリを作成しているのでこちらをご確認ください。

github.com

まとめ

AmplifyをVueに組み込んでみました。
CLIAWSリソースを作るのは便利ですが、CI/CDとの兼ね合いが難しそうです。

VueのAmplify UIはReactに比べてまだデザインの拡張性は難しいですが、簡単にAWSとの連携を実装できます。
AmplifyはOSSなので欲しい機能などはPRを出してみたいです。

参考:

Vue