「Laravel + Vue.jsではじめる 実践 GraphQL入門」の全貌を大公開します!〜GraphQL + VueでSPAフロントエンドを開発!(アカウント作成ページ/機能の実装)編〜
こんにちは。kzkohashi です。
FISM という会社でCTOをやっております。
今年4月に「Laravel + Vue.jsではじめる 実践 GraphQL入門」という技術書籍を出版しました。
前回からは、GraphQL + Vue でのSPAフロントエンド開発をお送りしております。
▼ 前回の記事はこちらをご覧ください💁🏿♂️
今回は、
どうぞご覧ください!
ApolloClientの設定をする
開発の土台が整いましたので、Apollo-clientの設定をしていきます。プロジェクトファイル内のmain.jsに記載していきます。
Apolloの仕様
Apollo-clientはreact-reduxを触っている人には、わかりやすいと思いますが 大元のProviderを作成し、親コンポーネントに読み込ませ、すべての子コンポーネントは親を参照できる仕組みです。
Vueの初期化タイミングでvue-apolloライブラリ経由で、Apollo-clientを読み込ませ、子コンポーネントで、通 信処理を実装できるようになります。
Apolloプロバイダの設定をしていきます
main.jsを開きます。twitter-like-client/src/main.js
まずmain.js上部にライブラリ群をインポートします
• twitter-like-client/src/main.js
// apollo本体
import { ApolloClient } from "apollo-client";
// ApolloClientへのオプション
import { setContext } from "apollo-link-context";
import { createHttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
// vueとの紐付け
import VueApollo from "vue-apollo";
main.jsの中部にapollo-clientとオプションの紐付けをしていきます。
コメントアウトにスクリプトに対する説明をしています。
// vueでvue-apolloを使用する事の紐付け
Vue.use(VueApollo);
// createHttpLinkでエンドポイントの指定をします
const httpLink = createHttpLink({
uri: "http://localhost:8000/graphql"
});
// ログイン後にBearerを指定したJWT通信が入るので先に記述します
// 今回はlocalstorageを用いてtokenを保持します
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem("vue_token");
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
}
});
// Apolloclientの初期化です
// linkとmemoryCacheを有効にします
const apolloClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
// apolloProviderを設定します。
// あとでstoreにエラーを通知しようと思うのでstoreへのコミットを記述します
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
errorHandler(error) {
store.commit("error");
}
});
// 最後にVueの初期化の際にapolloproviderを設定します
new Vue({
router,
store,
apolloProvider,
render: h => h(App)
}).$mount('#app')
記載内容の全て
import Vue from 'vue'
import './plugins/vuetify'
import App from './App.vue'
import router from './router'
import store from './store'
// apollo本体
import { ApolloClient } from "apollo-client";
// ApolloClientへのオプション
import { setContext } from "apollo-link-context";
import { createHttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
// vueとの紐付け
import VueApollo from "vue-apollo";
Vue.config.productionTip = false
// vueでvue-apolloを使用する事の紐付け
Vue.use(VueApollo);
// createHttpLinkでエンドポイントの指定をします
const httpLink = createHttpLink({
uri: "http://localhost:8000/graphql"
});
// ログイン後にBearerを指定したJWT通信が入るので先に記述します
// 今回はlocalstorageを用いてtokenを保持します
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem("vue_token");
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
}
});
// Apolloclientの初期化です
// linkとmemoryCacheを有効にします
const apolloClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
// apolloProviderを設定します。
// あとでstoreにエラーを通知しようと思うのでstoreへのコミットを記述します
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
errorHandler(error) {
store.commit("error");
}
});
// 最後にVueの初期化の際にapolloproviderを設定します
new Vue({
router,
store,
apolloProvider,
render: h => h(App)
}).$mount('#app')
Apolloを利用する準備が整いました
ここからサーバーサイドで実装したAPIを使い、アプリケーション作成に入ります。
アカウント作成ページ/機能の実装
ページ・機能の作成は大きく以下の手順で行なっていきます
- views フォルダに新しくページ用のvueファイルを作成
- routerにurlとページ用のvueの紐付けを記述
- ページ用vueファイルにDOMを記載
- GraphQLを記述
- ページ用vueファイルにロジックを記載
- 実行!
それでははじめましょう!
アカウント作成機能を実装する
viewsフォルダにSignup.vueを作成します
内容は以下のように記載しておきます
<template>
<div>アカウント作成ページ</div>
</template>
router.jsに追記
routesの配列にsignupのルートを追加
• twitter-like-client/src/router.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import Signup from './views/Signup.vue' // ここに追記
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name 'home',
component: Home
},
{ // ここに追記
path: "/signup",
name: "signup",
component: Signup
},
]
})
localhost:8080/signupにアクセス
Signup.vueに記載した内容が表示されていればOKです。
フォーム入力とsubmitイベントを記載していく
Signup.Vueを修正
vプレフィックスが付いているタグが Vuetify のタグです。
<v-form @submit="createAccount"onSubmit="return false;">
今回、ボタンのトリガーで実行するメソッド
createAccount と、submit時にリロードしてしまう仕様を止めるため onSubmit="return false;" を記載しています。
tampleteタグ部分
• twitter-like-client/src/views/Signup.vue
<template>
<v-form @submit="createAccount" onSubmit="return false;">
<v-container>
<v-layout>
<v-flex xs12 md4>
<v-text-field
v-model="name"
label="name"
required
></v-text-field>
<v-text-field
v-model="twitterId"
label="twitterId"
required
></v-text-field>
<v-text-field
v-model="email"
label="E-mail"
required
></v-text-field>
<v-text-field
v-model="pass"
label="password"
required
></v-text-field>
<v-text-field
v-model="passConf"
label="password_confirm"
required
></v-text-field>
<v-btn color="primary" type="submit">アカウント作成</v-btn>
</v-flex>
</v-layout>
</v-container>
</v-form>
</template>
GraphQLを記述するJSファイルの作成
GraphQLを記述するJSファイル作成していきます。
srcフォルダ直下にgraphqlフォルダを作成しました。
今回は、graphqlフォルダ内にmutation.js / query.jsを配置しました。
mutation.jsにGraphQLを記述します
graphql-tagライブラリを使用すると、JSのテンプレートリテラル形式でGraphQLを記載できます。
サーバーサイドで定義した、CreateAccountのmutationを記述します。
フロントエンドの場合、mutationで受け付けるvariablesの値は'mutaion(この中)'で受け付けます。 引数のような扱いで $ に変数としてvariablesを定 義し実際のCreateAccountの内部で変数を付与していきます。
• twitter-like-client/src/graphql/mutation.js
import gql from 'graphql-tag';
export const CREATE_ACCOUNT = gql`
mutation(
$name: String!
$twitter_id: String!
$email: String!
$password: String! $password_confirmation: String!
) {
CreateAccount(
name: $name
twitter_id: $twitter_id
email: $email
password: $password
password_confirmation: $password_confirmation
) {
account {
twitter_id
}
token {
access_token
token_type
expires_in
}
}
}
`;
Signup.vueにロジックを記載していきます
Signup.vueのdata作成
scriptタグをtemplateタグの下に記述していきます。
vuetifyのフォーム記述に則り、dataに対して、フォームのバリューを記載しています。※バリデーションを設定する事もできます
また、mutation.jsもインポートしておきましょう。
• twitter-like-client/src/views/Signup.vue
~
</template>
<script>
import { CREATE_ACCOUNT } from "../graphql/mutation.js";
export default {
data: () => ({
name: "",
twitterId: "",
email: "",
passConf: "",
pass: ""
})
}
</script>
script部分にcreateAccountが実行された時のロジックを書きます
ポイントとなるのは this.$apollo です!
Providerに設定して親のVueに設定したApolloクライアントは子のコンポーネントで this.$apollo という形で参照できます。
mutationの実行は this.$apollo.mutate() で実行します!
this.$apollo.mutateはオブジェクトを引数で取り、GraphQLの記述やvariablesを記載します。
以下のコードを 見るとお分かりかと思いますが、Promise形式でthenの中で通信後の記述が可能です。
~
</template>
<script>
import { CREATE_ACCOUNT } from "../graphql/mutation.js";
export default {
data: () => ({
name: "",
twitterId: "",
email: "",
passConf: "",
pass: ""
}),
methods: {
createAccount(e){
// mutation
this.$apollo.mutate({
// GraphQL
mutation: CREATE_ACCOUNT,
// Variables
variables: {
name: this.name,
twitter_id: this.twitterId,
email: this.email,
password: this.pass,
password_confirmation: this.passConf,
},
}).then((data) => {
// Sucess
console.log(data);
}).catch((error) => {
// Error
console.error(error)
});
}
}
}
</script>
Signup通信の準備ができたので実際に通信をしてみましょう
通信の際のチェックポイント
- Laravelのバックエンドサーバーは起動していますか?
- main.jsで記載したエンドポイントは正しいですか?
必要な入力項目を記載
ボタンを押してgraphQLが叩かれているか確認
画像のように、Devツールのnetworkにgraphqlの通信があれば成功です
エラーが出てしまった場合
筆者が経験した通信時のエラーなどになります。
以下チェックしてみると解決されるかもしれません。
原因となる事:
- エンドポイントが間違っている
- バックエンドに適切なクロスオリジン対応がなされていない
- バックエンド側のバリデーションに引っかかってる
- graphQLの記載 or variablesが間違っている or タイポがある
など
通信に成功していたらtokenをlocalstorageに保存してstoreにロ グイン状態を保存します
今回Vuexでの状態管理は、ログイン状態管理のみ行います。
store.jsにログイン状態を保持するためのスクリプトを記載します
Vuexの記述にも mutations がありますがこれはGraphQLとは関係ありません。 mutationsに記載されたメソッドは、あくまでstore側に状態を伝えた際に実行されるメソッド群となります。
今回 logined と error のメソッドを記述してますが、view側でログインした場合にstoreに対して store.commit ("logined") という形で状態を伝えます。 そうすると、下記に記載した logined メソッドが発火します。
• twitter-like-client/src/store.js
import Vue from "vue";
import Vuex from "vuex";
import router from "./router";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
logined: localStorage.getItem("vue_token") ? true : false,
error: false
},
mutations: {
logined(state, payload) {
state.logined = true;
},
error(state, payload) {
// main.jsに記載されたGraphQLのグローバルエラーをハンドリングする
router.push("/login");
}
}
});
Signup.vueの通信成功時にtopページを表示されようにする
まずはSignup.vueでログイン成功をvuexに伝え、ページ切り替え処理を
入れる
まずは、 store.js をインポートします。 そして、 .then((data) => { からの記述が通信成功時の処理ですので、そこに
const token = localStorage.setItem("vue_token", data.data.CreateAccount.token.access_to ken);
store.commit("logined");
this.$router.push("/");
- ローカルストレージへの保存
- ストアへの状態送信
- ルーターへのページ切り替え処理
を記載しています。
• twitter-like-client/src/views/Signup.vue
<script>
import { CREATE_ACCOUNT } from "../graphql/mutation.js";
import store from "../store.js"; // storeをインポート
export default {
data: () => ({
name: "",
twitterId: "",
email: "",
passConf: "",
pass: ""
}),
methods: {
createAccount(e){
//mutation
this.$apollo.mutate({
// GraphQL
mutation: CREATE_ACCOUNT,
// Variables
variables: {
name: this.name,
twitter_id: this.twitterId,
email: this.email,
password: this.pass, password_confirmation: this.passConf,
},
}).then((data) => {
// Sucess
// tokenの保存および、storeへ状態を送信、ページの切り替え
console.log(data);
const token = localStorage.setItem("vue_token", data.data.CreateAccount.token.
access_token);
store.commit("logined");
this.$router.push("/"); }).catch((error) => {
// Error
console.error(error)
});
}
}
}
</script>
これでSignup.vueの記述は終わりです。すべてのスクリプトはこちらです
<template>
<v-form @submit="createAccount" onSubmit="return false;">
<v-container>
<v-layout>
<v-flex xs12 md4>
<v-text-field
v-model="name"
label="name"
required
></v-text-field>
<v-text-field
v-model="twitterId"
label="twitterId"
required
></v-text-field>
<v-text-field
v-model="email"
label="E-mail"
required
></v-text-field>
<v-text-field
v-model="pass"
label="password"
required
></v-text-field>
<v-text-field
v-model="passConf"
label="password_confirm"
required
></v-text-field>
<v-btn color="primary" type="submit">アカウント作成</v-btn>
</v-flex>
</v-layout>
</v-container>
</v-form>
</template>
<script>
import { CREATE_ACCOUNT } from "../graphql/mutation.js";
import store from "../store.js";
export default {
data: () => ({
name: "",
twitterId: "",
email: "",
passConf: "",
pass: ""
}),
methods: {
createAccount(e){
//mutation
this.$apollo.mutate({
// GraphQL
mutation: CREATE_ACCOUNT,
// Variables
variables: {
name: this.name,
twitter_id: this.twitterId,
email: this.email,
password: this.pass, password_confirmation: this.passConf,
},
}).then((data) => {
// Sucess
console.log(data);
const token = localStorage.setItem("vue_token", data.data.CreateAccount.token.
access_token);
store.commit("logined");
this.$router.push("/");
}).catch((error) => {
// Error
console.error(error)
});
}
}
}
</script>
コラム
VuexのstoreでGraphQL通信処理の記述はしないの?
Vuexを使用したVueアプリケーションの開発ではVuex側(store.jsなど)で通信処理を書くことが多いと思います。
今回は、 vue-apollo を利用した開発という事で、コンポーネントでの通信を中心に解説できればと思い、Vuex側でGraphQL通信をすることはやめました。
Store側でのGraphQL通信をする場合、Vue-apolloを使わなくてもaxiosで十分という理由もあります。大規模な開発をしていく場合やすでにVuexを使用したプロジェクトの場合などは Vuexのstore側で通信した方が開発しやすいかもしれません。
✂︎ ---------------------
いかがでしたでしょうか?
13000字近い内容をお読みいただき、ありがとうございます!
次回も木曜日に、公開いたします!
ぜひ見ながら、実際に手を動かしていただけるとなおうれしいです!
引き続きご覧くださいませ。
Fin.
▼ Twitterもやってます。よければフォローもお願いします🙇🏿♂️
▼ FISM社についてはこちら💁🏿♂️
▼ 現在Wantedlyにて開発メンバー募集中です!GraphQL + Laravel + Vue.js + Swift で開発しております👨🏿💻まずはお気軽にお話ししましょう🙋🏿♂️
この記事が気に入ったらサポートをしてみませんか?