nuxt.js + firebase でPWA,SSRな借りパクを撲滅するアプリを作っている話

この記事は[Nuxt.js #2 Advent Calendar 2018 16日目](https://qiita.com/advent-calendar/2018/nuxt-js-2)の記事です。

某webマーケティング会社でフロントエンドエンジニアとして働きながら、バンド活動もおこなっている[taroooooooooo](https://twitter.com/taroooooooooo)です。
バンド活動をしていると何かと支払いのタイミングが多く、バンドメンバー間での一時的な金銭などの貸し借りが多く発生してしまいます。
今まではLINEのグループ内にメモとして残していたのですが、どこまでが返却済みなのかを把握するのが難しかったためアプリを作ることを決意しました。

この記事ではアプリの概要についてとfirebaseの認証・ルーティング周りの話をします。

firestore周りの話はこちらに書きました。
[firestoreのSecurity Ruleを設定して特定のuserのみが閲覧できる設計をする](https://qiita.com/taroodr/items/887c2d965fad39ce3c7c)


# アプリの概要
貸し借りを可視化するという意味をこめて「Cashica」というサービス名にしました。
まだまだ制作中でデザインやUIは改善予定です。（スマホでの利用を前提としているのでPCで見るとデザインの崩れがひどい。。）
機能としてはシンプルでユーザ間の貸し借りが登録、削除できるだけというものです。
友達登録にはQRコードの読みこみで対応しています。
下記にソースコードとsampleを置いていますのでご覧ください。

技術的にはnuxt.jsとfirebaseのfirestore, authentication, cloudfunctionを使っている構成になっています。

<img width="375" alt="easypay-dd04a.firebaseapp.com_(iPhone 6_7_8).png" src="https://qiita-image-store.s3.amazonaws.com/0/201963/54e6258f-78a3-1a28-6746-43780d5bb605.png">

GitHub: https://github.com/taroodr/cashica
Demo: https://easypay-dd04a.firebaseapp.com

# 認証について
以下のように認証情報取得用のpluginを準備します。
async,awaitで呼び出せるようにfirebase.auth().onAuthStateChanged()メソッドを直接呼ぶのではなくpromiseのラッパーを作ると便利になります。

```javascript:~/plugins/auth.js
import firebase from '~/plugins/firebase'

function auth() {
  return new Promise((resolve, reject) => {
    firebase.auth().onAuthStateChanged(user => {
      resolve(user || false)
    })
  })
}
export default auth
```
このpluginをコンポーネントがmountされた際に下記のように使うことで、認証情報がvuexに格納されていない場合に認証情報を取得できます。

```javascript:~/pages/signin.vue
import auth from '~/plugins/auth'

export default {
  async mounted() {
    let user
    if (!this.user) user = await auth()
    await Promise.all([
      this.user
        ? Promise.resolve()
        : this.$store.dispatch('SET_CREDENTIAL', { user: user || null })
    ])
  }
```
認証情報の取得はsignin.vueのみで行なっている設計になっているのですが、これは認証状態によってリダイレクトを行うmiddlewareによってかならず/signinにリダイレクトされる仕様になっているためです。

```javascript:~/middleware/authentiated.vue
export default function({ store, route, redirect }) {
  if (!store.getters.isAuthenticated && route.name !== 'signin') {
    redirect('/signin')
  }
  if (store.getters.isAuthenticated && route.name === 'signin') {
    redirect('/')
  }
}

```
middlewareはnuxt.config.jsに下記のように記載することでルートの変更を検知するたびにmiddlewareの内容を実行してくれます。

```javascript:nuxt.config.js
export default {
  router: {
    middleware: 'authenticated'
  },
}
```

再読み込みしたタイミングで一度`/signin`にリダイレクトされてしまうので、認証情報の取得が終わるまでloading用のコンポーネントを表示をおこなっています。

```javascript:~/pages/signin.vue
export default {
  async mounted() {
    let user
    if (!this.user) user = await auth()
    await Promise.all([
      this.user
        ? Promise.resolve()
        : this.$store.dispatch('SET_CREDENTIAL', { user: user || null })
    ])
    if (user) {
      this.$router.push('/')
    } else {
      this.loadComplete()
    }
  },
```
## 疑問
上記の実装だと、必ず/signinに一度リダイレクトされてしまうのが気持ち悪いのですが何か解決方法はないのでしょうか？
詳しい方いらっしゃいましたら教えてください:clap::clap:


# やり残したこと
1週間ほど前から作業を行なっていたのですが、想像以上に進捗がよろしくなく必須だと考えていたpush通知機能をつけることができませんでした。。。
firebase cloud messagingを使うと楽に実装できそうですので、完成し次第また記事を書こうと思います。


# 雑感
- すべてのページが認証ありでないと入れない構成になっていたので、わざわざSSRする必要はなかった。
- ただ、SSR対応自体は[【v2対応しました】Nuxt.jsとFirebaseを組み合わせて爆速でWebアプリケーションを構築する](https://qiita.com/potato4d/items/cfddeb8732fec63cb29c)を参考に簡単に実装できた。

# 参考文献
制作にあたり下記記事には大変お世話になりました。
https://qiita.com/potato4d/items/cfddeb8732fec63cb29c
https://blog.shimar.me/2018/03/31/nuxt-firebase-authentication.html