今更”Nuxt Firebase”っていうものを知ったわ

firebase
Gerd AltmannによるPixabayからの画像

目的

久々にNuxtでなんか作ってみようと色々ネットを漁ってたら

Nuxt Firebase

っていうモジュールがあることを知りました。

NuxtとFirebase自体はそれぞれ知っています。
だって以前Nuxt, Firebaseに関する記事を書いていますからね~

ただ確かそのときはFirebaseをインストールした後、Firebaseのフォルダを用意してその中にFirebase関連の単体機能を実装し、使うときにそこからインポートして使っていたような気がする
(記憶があいまいですが・・)

ところが久々にFirebase関連の情報を探していたところNuxtでFirebaseを簡単に統合できるモジュールがあることを知ってこれはいろいろ試して学んだところまでレポートとしてまとめようと思いました。

開発環境

  • Windows10 Home 64bit
  • Nuxt.js 2.15.3
  • firebase 8.6.3
  • javascript

今回手を加えたファイル

fetch_app/
  ├ middleware/
  │   └ auth.js
  ├ pages/
  │   ├ secondPage/         
  │   │    └ index.html
  │   └ index.html       
  ├ plugins/   
  │   └ firebase/
  │       └ auth.js
  ├ store/
  │   └ index.js
  ├ .env
  └ nuxt.config.js

準備

とりあえずNuxtは簡単にセットアップできるでしょう

Node.jsがすでに導入済みということ前提でnpm, npxはすでにあるはずなので
適当なディレクトリで下のコマンドを実行しましょうか
(私はVScodeのターミナルで実行しましたが何でも良いです)

npx create-nuxt-app fetch_app

この辺は詳しいことは省きます詳細を知りたい人は下のリンクで確認してください

次はfirebase@nuxtjs/firebaseインストールします。

@nuxtjs/firebaseだけをインストールするだけじゃダメみたいですね

npm install firebase
npm install @nuxtjs/firebase

インストールが完了したら今度はnuxt.config.jsでfirebaseの設定をします。

export default {
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  modules: [
    '@nuxtjs/firebase'
  ],

  firebase: {
    config: {  // ...1
      apiKey: process.env.API_KEY,
      authDomain: process.env.AUTH_DOMAIN,
      projectId: process.env.PROJECT_ID,
      storageBucket: process.env.STORAGE_BUCKET,
      messagingSenderId: process.env.MESSAGING_SENDER_ID,
      appId: process.env.APP_ID
    },
    services: {
      auth: true  // ...2
    }
  },
}
  • 1: ここでfirebaseの初期設定をします。
    @nuxtjs/firebaseを使用する前は下のようにjsファイルで初期化して利用していましたが、今回の場合configファイルでキーやIDを入れるだけなのでめちゃくちゃ簡単です。
    また、apikeyなどに設定する値は.envファイルに書き込んでいるのでconfig.jsファイルにはprocess.env.~で設定しています。
// 従来の書き方
import firebase from 'firebase/app';

const firebaseApp = firebase.initializeApp({
  apiKey: ~~~~~~,
  authDomain: ~~~~~~,
  projectId: ~~~~~~,
  storageBucket: ~~~~~~,
  messagingSenderId: ~~~~~~,
  appId: ~~~~~~
});
  • 2: ここではfirebaseの各サービスの中で利用したいサービスの設定をしています。
    今回は認証機能だけを使いたいのでとりあえず、authtrueで設定します。
    細かい設定は後に・・・

.envファイルの書き方は説明不要かと思いますが一応
~~~~~の部分は各々のfirebaseの設定を確認してください

API_KEY = ~~~~~~~~~~~~~~~~~~~
AUTH_DOMAIN = ~~~~~~~~~~~~~~~~~~~
PROJECT_ID = ~~~~~~~~~~~~~~~~~~~
STORAGE_BUCKET = ~~~~~~~~~~~~~~~~~~~
MESSAGING_SENDER_ID = ~~~~~~~~~~~~~~~~~~~
APP_ID = ~~~~~~~~~~~~~~~~~~~

firebaseの認証機能を利用したサインイン、サインアウト機能を実装

ではサインイン、サインアウト機能を作っていきます。

サインイン機能の実装

// fetch_apps/plugins/firebase/auth.js

// 指定した値ミリ秒後に実行する
const timer = (millisecond) => {
  return new Promise((resolve) => {
    const timerID = setTimeout(() => {
      resolve(timerID)
    }, millisecond)
  })
}

const retry = async (context) => {  // ... 1
  await timer(300)  // ... 2
  if (context.store.getters.isAuthenticated) {  // ... 3
    return context.redirect('/secondPage')
  } else {
    retry(context)
  }
}

const signIn = (context, email, password) => {
  context.app.$fire.auth.signInWithEmailAndPassword(email, password) // ... 4
    .then(() => {
      retry(context)
    })
    .catch((error) => {
      // eslint-disable-next-line no-console
      console.log(error)
    })
}

export default (context, inject) => {
  inject('signInWithEmailAndPassword', (email, password) => signIn(context, email, password)) // ... 4
}

  • 1: 一定時間後に認証状態を確認し、認証済みだったら目的のページ(今回は/secondPage)にリダイレクト、認証が確認できなかったら再度retryし直す機能です。
    この機能無しだと、サインインした際に取得した認証情報をstoreに保存するよりも前にリダイレクトしてしまい結果サインインページに戻されてしまうためです。
    ※もしかしたらstoreに保存された状態を監視する方法が用意されているかもしれませんが現時点で見つけれていないので見つかり次第、別途書き残します。
  • 2: ここで0.3秒ほど待ちます。
    早い時だとぎりぎりこのくらいで認証情報がstoreに保存し終わるのでサインインに成功します。
  • 3: ここでstoreに認証情報が保存されているか確認しています。
    pluginsで扱えるcontextからstoreを参照できるのでgettersで認証状態を確認します。
    まだ現時点ではstoreの設定が済んでいないのでまだ参照できません。
  • 4: contextからfirebaseの機能を呼び出すことができるのでsignInWithEmailAndPasswordを呼び出します。
    使い方は今までと同じように扱えばOKです。
  • 5: 外部ファイルで扱うためにサインインの機能を登録しておきます。
    以前はjsファイルで作成したらexportして別ファイルでimportすることで使っていました。
    @nuxtjs/firebaseでは登録することでインポートしなくても使えるようになります。

サインアウトの実装

// fetch_apps/plugins/firebase/auth.js

const timer = (millisecond) => {
~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~
}

const retry = async (context) => {
~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~
}

const signIn = (context, email, password) => {
~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~
}

const signOut = (context) => {
  context.app.$fire.auth.signOut() // ... 1
  return context.redirect('/') // ... 2
}

export default (context, inject) => {
  inject('signInWithEmailAndPassword', (email, password) => signIn(context, email, password))
  inject('signOut', () => signOut(context))
}
  • 1: サインアウトするときは通常のfirebaseを利用するときと同様にsignOut()を呼び出すだけでできます。
    ただ今回はcontextから呼び出すためcontext.appの$fireから呼び出しています。
    詳しくは以下を参照
  • 2: ここでリダイレクトしていますがNuxtの仕組み的にpagesフォルダ直下に置いているindex.vueファイルが表示されます。

ログイン画面と遷移先画面を作成

ログイン画面

サインインとサインアウトの機能を用意できたのでまずログイン画面を作ります。

<template>
  <!-- fetch_apps/pages/index.vue template部分 -->
  <div class="container">
    <div>
      メールアドレス: <input v-model="email"> <!-- ... 1 -->
      パスワード: <input v-model="password">
      <button @click="login(email, password)"> <!-- ... 2 -->
        送信
      </button>
    </div>
    <NuxtLink to="/secondPage"> <!-- ... 3 -->
      Go to secondPage
    </NuxtLink>
  </div>
</template>
  • 1: ここがユーザーの情報を入力する部分なのですがここに値を入力すると自動でemail, passwordに値が代入されます。
    このemail, passwordという変数は後に出てくる<script></script>内で用意する必要があります。
  • 2: @clickはここに関数を入れるとこの<button></button>で作られたボタンをクリックした際に定義した関数が呼び出されます。
    ここではlogin関数を紐づけていますがこの関数もscript内で定義しています。
  • 3: これは遷移先ページに移動するリンクを書いていますが、これはNuxtの標準機能です。
    pagesフォルダ内の各ページに移動する際に便利な機能ですが詳しくは下の公式ドキュメントを読んでみてください
<script>
// fetch_apps/pages/index.vue script部分
export default {
  name: 'Main',
  data () {
    return {
      email: '',
      password: ''
    }
  },
  methods: {
    login (email, password) {
      this.$signInWithEmailAndPassword(email, password)  // ... 1
    }
  }
}
</script>
  • 1: サインイン、サインアウト機能を実装したときにそれらの関数を登録していましたが、
    methods内でthisから呼び出すことができます。
    以前はimportして呼び出していましたが、import文が不要になったことで少しだけコード量が減ってすっきりしましたね~

遷移先ページ(/secondPage)作成

ログイン後の遷移先ページを作ります。

<template>
  <!-- fetch_apps/pages/secondPage/index.vue template部分-->
  <div class="container">
    <div>
      Second Page
    </div>
    <div>
      <button @click="logout">
        ログアウト
      </button>
    </div>
  </div>
</template>

template部分は特に説明することが無いので省略します。

<script>
// fetch_apps/pages/secondPage/index.vue script部分
export default {
  name: 'SecondPage',
  middleware: 'auth',  // ... 1
  methods: {
    logout () {
      // @ts-ignore
      this.$signOut()
    }
  }
}
</script>
  • 1: 遷移先ページはログインしたユーザーだけが表示できるようにしたいので後に用意する認証状態を確認する機能を実装します。
    middlewareで実装していきます。

ユーザーの認証状態を確認する機能

先ほど作った遷移先ページをログインしたユーザーだけが表示されるようにする機能を実装していきます。

// fetch_apps/middleware/auth.js
export default function ({ store, redirect }) {
  if (!store.getters.isAuthenticated) {  // ... 1
    return redirect('/')
  }
}
  • 1: middlewareではcontext内のstoreを参照することができます。
    後に設定しますがログインしたときに生成された認証情報をstoreに保存し、gettersを実装しますのでそこから認証情報を取得して確認します。

認証情報をstoreに保存

認証情報を保存する処理を実装します。

export const state = () => ({
  user: null
})

export const getters = {
  isAuthenticated (state) {  // ... 1
    return !!state.user
  }
}

export const mutations = {
  setUser (state, { authUser }) {  // ... 2
    if (!authUser) {
      state.user = null
    } else {
      const { uid, email, emailVerified } = authUser
      state.user = { uid, email, emailVerified }
    }
  }
}
  • 1: ログイン後取得した認証情報がstate内に保存されているかを確認し、認証情報があったらtrueを返します。
  • 2: 取得した認証情報をstate.userに入れていますが、公式を見ると取得した認証情報(authUser)をstate.userに直接入れてはいけないみたいです。
    uid, email, emailVerifiedの三つだけ登録しています。

実装したmutationssetUserはログイン後実行されるのですが、直接呼び出す処理を実装しません。

どうするかというと「準備」内のfirebaseのauthで設定します。

export default {
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  modules: [
    '@nuxtjs/firebase'
  ],

  firebase: {
    config: {  // ...1
      apiKey: process.env.API_KEY,
      authDomain: process.env.AUTH_DOMAIN,
      projectId: process.env.PROJECT_ID,
      storageBucket: process.env.STORAGE_BUCKET,
      messagingSenderId: process.env.MESSAGING_SENDER_ID,
      appId: process.env.APP_ID
    },
    services: {
      auth: {
        initialize: {
          onAuthStateChangedMutation: 'setUser'  // ... 1
        }
      }
    }
  },
}
  • 1: ログインして認証状態が変わったときにstoreのmutationsで定義した関数を呼び出してくれます。
    ここに先ほどのsetUserを設定することでログイン後自動で呼び出して認証情報を保存してくれます。

まとめ

今回の実装の問題点は遷移先ページでリロードするとログイン画面に戻されてしまいます。
ですが今回はNuxt/Firebaseを試してみた程度なのでこの辺で一旦終わります。

firestoreやstorage等使えるサービスはほかにもあるのでもっと調べてどんどん利用していくのもありかもしれません。

この記事が何かしら参考になれば幸いです。

ここまで読んでくれてありがとう!!

タイトルとURLをコピーしました