😊

令和最新版 firebase + nuxt3のwebアプリ開発手順④

2022/01/09に公開約4,300字

全体像

  1. Nuxt3アプリ作成〜firebaseデプロイ
  2. TailwindCSSの導入
  3. ログイン機能の実装
  4. firestoreを使ったデータの読み取り、書き込み、更新

設計概要

firebaseの関数はサーバー側で実行します。

  • servers/apiにfirebaseの初期化処理や各コンポーネントで利用する関数を配置します。
  • 各コンポーネントからはuseFetchでデータを取得できます。

※以下リンクで紹介されている方法とほとんど同じです
https://enterflash.io/posts/connect-to-firestore-db-using-admin-sdk-nuxt-3--firebase

※firebaseの処理をフロント側で実行する方法もあります。
しかし、フロント側で実行する方法では、ページ読み込み時にdbオブジェクトを利用する方法を確立できなかったため、今の私はサーバー側で実行する方法をとっています。
https://zenn.dev/yuta_enginner/articles/f2eb0d4f36dfc7

フロント側で実行する方法ではSDKでconfig({apiKey:~~~,authDomain:~~~と書いてあるやつ)でinitializeAppを実行できましたが、サーバー側で実行する方法では秘密鍵でinitializeAppを実行します。SDKで実行できないので注意。

どういったアプリか

  • ユーザー認証はfirebase authのメール認証を用いています。
  • dbはfirestoreを用い、データは以下のような鬼滅の刃のキャラクターです。

秘密鍵の取得

  • firebaseコンソールで秘密鍵(jsonファイル)を取得します。
  • アプリのルートディレクトリに秘密鍵のjsonファイルを保存
  • gitignoreに記載することを忘れずに

server/api/firebase.ts

import { getFirestore } from 'firebase-admin/firestore'
import { initializeApp, getApps, cert } from 'firebase-admin/app'

const apps = getApps()

if (!apps.length) {
    initializeApp({
        credential: cert('./himitsukagi.json') // ../../himitsukagiではなく、./です
    })
}

export default async (request, response) => {
    const db = getFirestore()
    const usersSnap = await db.collection('users').get()
    const usersData = usersSnap.docs.map(doc => {
        return {
            id: doc.id,
            ...doc.data()
        }
    })
    
    return usersData
}

これでコンポーネントからは以下のようにしてデータを取得できます

const {data:usersData} = await useFetch('/api/firebase')

app.vue

クリックで表示
<script setup lang="ts">
type page = {
  title:string,
  icon:string,
  to:string
}

const pages = <page[]> [
  {
    title:'firestoreテスト',
    icon:'',
    to:'/dbtest'
  },{
    title:'authテスト',
    icon:'',
    to:'/authtest'    
  }
]

</script>

<template>
    <div class="w-screen h-screen flex flex-col">
        <nav class=" bg-cyan-200 w-full flex justify-between h-12 px-4 py-2" >
            <div class="text-xl font-bold"> ナビゲーション</div>
            <ul class="flex self-end">
              <li v-for="menu of ['Home','About','Services','Pricing','Contact']">
                <div class="mx-4">{{menu}}</div>
              </li>
            </ul>
        </nav>

        <div class="flex h-full">
          <aside class=" w-48 bg-gray-200">
              <ul>
                  サイドメニュー
                  <li v-for="page in pages">
                      <button class="my-3 px-3 text-center">
                        <NuxtLink :to="page.to">{{page.title}}</NuxtLink>
                      </button>
                  </li>       
              </ul>
          </aside>

          <main class="w-full">
            <div class="bg-lime-100 h-full">
                <NuxtPage></NuxtPage>              
            </div>
          </main>
        </div> 
    </div>
</template>

pages/dbtest.vue

クリックで表示
<script setup lang="ts">
type User = {
    id:String,
    name:String
}
type Users = Array<User>

const {data:users} = await useFetch('/api/firebase')

</script>

<template>
    <h2 class=" text-2xl">firestoreテスト</h2>
    {{users}}
</template>

それにしてもuseFetchを使うと、めちゃくちゃシンプルに書けますね

ビルドとエミュレーターテスト

yarn run devで動くことが確認できたら、エミュレーターでテストしましょう

$ NITRO_PRESET=firebase yarn build
$ firebase emulators:start

おそらくdbtestのページを開こうとすると失敗する(ページ自体は開くがデータを取得できない)はずです。

エラーメッセージを見ると、秘密鍵のファイルがありませんと出ています。

エラー元になっている次のファイルを見てみましょう。
.output/server/chunks/firebase.mjs
さっきまで書いていたserver/api/firebase.tsと同じ内容です。

yarn run devで動かしていたときはserver/api/firebase.tsから秘密鍵のjsonファイルを参照していましたが、firebase emulatorsで動かしているときは.output/server/chunks/firebase.mjsから秘密鍵のjsonファイルを探そうとしているため、見つからないはずです。

対処法としては、.output/serverディレクトリに秘密鍵のjsonファイルを入れてやることで、エミュレーターテストやデプロイで問題なく動くようになります。

しかし、それだとビルドのたびに秘密鍵のjsonファイルが消えるので、毎回秘密鍵のjsonを貼り付ける必要があります。

対処法募集
そこで、functionsの環境変数に秘密鍵のjsonの内容を記載してやりました。

$ firebase functions:config:set credential="・・・"

server/api/firebase.json

+import * as functions from 'firebase-functions'

if (!apps.length) {
+    const config = functions.config()

    initializeApp({
+        credential: config.credential? config.credential:cert('./engineer-house-firebase-adminsdk-iju2g-98ad0c900c.json')
    })
}

しかし現状はfunctions.config().credentialを認識してくれません。
解決法募集しています。

作成、更新、削除は追って作成します

Discussion

ログインするとコメントできます