GraphQL と Nuxt.js でチャットを作る
目次
この記事はNuxt.js #2 Advent Calendar 2018の18日の記事です。
前の記事はNuxtでApolloを使わずにGraphQLを扱ってみる です。GraphQLの記事が続きますね。
はじめに
近年、GraphQLの知名度も上がり、RESTfulAPI に疲弊したプロジェクトがGraphQLを採用することが多くなっていると感じます。
GraphQLの詳しい説明は省略しますが、データ読み込みのquery, データ書き込みのmutation、リアルタイム接続のsubscriptionがあります。
query, mutationに比べてsubscriptionについての記事が少ないと感じたため、この記事ではSubscriptionを使用した簡単なチャットをNuxt.jsで作成します。
Demo
画面上は一般的なチャットですが、GraphQLのSubscriptionを使用しています。
githubにソースをあげていますので、そちらも参考にしてください。
GraphQLについて
バックエンドはGolangのgo-gqlgenを使用してGraphQLを実装しています。
GolangのAdventCalendarではないので省略しますが、主要なスキーマは以下のようになっています。
type
type Message {
id: String!
user: String!
createdAt: Time!
text: String!
}
query
users [String!]!
messages: [Message!]!
subscription
messagePosted(
user: String!
): Message!
userJoined(
user: String!
): String!
Nuxt.jsの話
今回は、Nuxt.jsとapolloを使用します。デザインは最近イチオシと聞くBuefyです。
準備
Nuxtのプロジェクトを作成後を前提として、apolloをインストールします。githubを参考にインストールします。
npm install --save @nuxtjs/apollo
npm install --save graphql-tag
nuxt.config.js に以下を追加します。
module.exports = {
modules: [,
'nuxt-buefy',
'@nuxtjs/apollo', // added
],
// added
apollo: {
clientConfigs: {
default: {
httpEndpoint: 'http://GraphQLのサーバ/query',
wsEndpoint: 'ws://GraphQLのサーバ/query',
websocketsOnly: true,
},
}
}
チャットに参加
本来は認証をちゃんとしなければならないのですが、簡単にするためStoreに保存しています。
<template>
<div class="modal-card">
<section class="modal-card-body">
<h3 class="title has-text-centered has-text-dark">Create User</h3>
<div class="box">
<b-field label="UserName">
<b-input v-model="user" type="user" placeholder="user name">
</b-input>
</b-field>
<button class="button is-dark is-large is-fullwidth" @click="join()">
Join
</button>
</div>
</section>
</div>
</template>
<script>
export default {
name: 'LoginModal',
data () {
return {
user: '',
}
},
methods: {
join() {
this.$store.commit('users/join', this.user)
this.$router.push('/chat')
}
}
}
</script>
チャットの画面
主要なところを抜粋します。
ユーザ一覧
<template>
<div>
ユーザ一覧
<b-taglist>
<b-tag type="is-light" size="is-large" v-for="user in users" :key="user" :style="isSelf(user)">{{user}}</b-tag>
</b-taglist>
</div>
</template>
<script>
import QUsers from '@/apollo/queries/users.gql'
import SUserjoined from '@/apollo/subscriptions/userJoined.gql'
export default {
apollo: {
users: { // ★1
query: QUsers, // ★2
subscribeToMore: { // ★3
document: SUserjoined, // ★4
variables () {
return {
user: this.$store.state.users.user
}
},
updateQuery: (prev, { subscriptionData }) => { // ★5
if (!subscriptionData.data) {
return prev
}
const user = subscriptionData.data.userJoined
if (prev.users.find(u => u === user)) {
return prev
}
return Object.assign({}, prev, {
users: [user, ...prev.users],
})
}
},
}
}
</script>
★1で★2のQueryを呼びます。★2の結果がHTML側で★1のプロパティ(ここではusers)を使用することができます。
★3からsubscriptionについての処理になります。
★4は下記のsubscriptionsを指定しており、データが更新されたら★5の処理を行います。
# users.gql
query {
users
}
# userJoined.gql
subscription($user: String!) {
userJoined(user: $user)
}
メッセージ一覧
メッセージ一覧もユーザ一覧と同様に、クエリにsubscribeToMoreプロパティを追加するようにします。
<script>
export default {
apollo: {
query: QMessages,
subscribeToMore: {
document: SMessagePosted,
variables () {
return {
user: this.$store.state.users.user
}
},
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) {
return prev
}
const message = subscriptionData.data.messagePosted
if (prev.messages.find(m => m.id === message.id)) {
return prev
}
return Object.assign({}, prev, {
messages: [message, ...prev.messages],
})
}
},
}
}
まとめ
数行nuxt.config.jsに追加し、subscribeToMoreを設定するだけで簡単にGraphQLのsubscriptionが使用できます。
websocketでAPIを設計するときはGraphQLも視野に入れてもいいと思います。
余談ですが、簡単なAPIを作成するのにGolangは向いていると思います。