Nuxt.jsでfirestoreのデータ取得をSSRで行うには?

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

目的

nuxt.jsでfirestoreのデータを取得するときに困ったことが起きました。それは……
セキュリティルールが設けられているfirestoreのデータを

サーバーサイドで取得する時どうすんの?

って話です。

セキュリティルールとはfirestoreからデータを取得する際、条件付きでデータを取得することができるユーザーを限定できるようにするためのルールです。

今回扱うコレクションは”users“で下のようにユーザーIDを持つユーザのみアクセスできるようにしています。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{id} {
      allow read, write: if request.auth.uid != null;
    }
  }
}

セキュリティルールを設けていない、またはクライアント側でデータの取得を行う場合は以下のURLのようにfirebaseモジュールを初期化すればデータを取得することができる。

しかし、サーバー側でデータの取得を行おうとすると

ERROR  Missing or insufficient permissions.                                                                             14:33:51
at new FirestoreError (node_modules\@firebase\firestore\src\util\error.ts:166:5)
at JsonProtoSerializer.fromRpcStatus (node_modules\@firebase\firestore\src\remote\serializer.ts:130:12)
at JsonProtoSerializer.fromWatchChange (node_modules\@firebase\firestore\src\remote\serializer.ts:433:40)
at PersistentListenStream.onMessage (node_modules\@firebase\firestore\src\remote\persistent_stream.ts:568:41)
at node_modules\@firebase\firestore\src\remote\persistent_stream.ts:448:21
at node_modules\@firebase\firestore\src\remote\persistent_stream.ts:501:18
at node_modules\@firebase\firestore\src\util\async_queue.ts:285:14
at processTicksAndRejections (internal/process/task_queues.js:93:5)

上記のエラーが発生してしまう。<br>今回はこの問題を解決します。

※この記事は以前はてなブログで上げていた記事と同じです。

環境

  • OS: Windows10 home
  • エディタ: Visual Studio Code

ディレクトリ構成

fadmin_test(任意のプロジェクトの名前)/
├ modules/  <= デフォルトでは存在しないので新たに追加してください
│      └ get_data.ts
├ pages/
│      └ index.ts
└ utils/  <= デフォルトでは存在しないので新たに追加してください
   ├ path/
   │   └ serviceAccountKey.json
   └ firebaseAdmin.ts

※今回使用するファイルのみ表示しています。

解決方法

結論から言うとfirebase-adminを使えば良いということになります。
firebase-adminを追加したいディレクトリに移動し下記のコマンドを実行する

C:\fadmin_test> npm install firebase-admin

次にこの状態でfirebaseサービスにアクセスするためには秘密鍵というjson形式のファイルが必要なのでそれを取得します。
まずはfirebaseのコンソール画面に行きます。

下画像からプロジェクトを設定をクリック

下画像のサービスアカウントを選択

下画像の新しい秘密鍵の生成をクリック後”公開リポジトリには保存するな“の注意が表示されキーを生成をクリックしダウンロードします。

ダウンロードしたjsonファイルはプロジェクト(ここではfadmin_test)のutils/path内に保存し、
ファイル名を”serviceAccountKey.json“に変えます

次にfirebase-adminを初期化します。

// utils/firebaseAdmin.ts
let admin
if (process.server) {
  admin = require('firebase-admin')
  if (!admin.apps.length) {
    const serviceAccount = require('./path/serviceAccountKey.json')  // ①
    admin.initializeApp({
      credential: admin.credential.cert(serviceAccount),  // ②
      databaseURL: 'データベースのURL'  // ③
    })
  }
}
export {
  admin
}
  • ①:ここで生成した秘密鍵を指定し、②で認証に使います。
  • ③:ここには通常のデータベースを使用する際に扱うdatabaseURLを記入します。
    (下画像の緑マーカー部分)

次にfirestoreからデータを取得するためのファイルを作ります。
firebaseモジュールをimportする場合とやり方は変わりません。

// modules/get_data.ts
import * as fadmin from '../utils/firebaseAdmin'

const get_user = async () => {
  return new Promise(callback => {
    fadmin.admin.firestore().collection('users').get()
    .then((query: any) => {
      const items: any[] = []
      query.forEach((doc: any) => {
        const item = {
          id: doc.id,
          data: doc.data()
        }
        items.push(item)
      })
      console.log(items)  // <= この部分がターミナル上に表示される
      callback(items)
    })
  })
}
export {
  get_user
}

次に画面に表示されるpagesフォルダの"index.vue"から"get_data.ts"を呼び出しデータを取ってきます。

<!-- modules/get_data.ts  <template> ~ </template>部分-->
<section>
  pages.index
  <div>
  {{ view }}
  </div>
</section>
// modules/get_data.ts  部分
import Vue from 'vue'
import { get_user } from '../modules/get_data'

export default Vue.extend({
  async asyncData ({ params }) {  // ①
    return {
      view: await get_user()
    }
  }
})
  • ①:サーバーサイドでデータを取得するのが今回の目的なのでasyncDataを用います。

最後以下のコードを実行する

C:\fadmin_test> npm run dev

するとターミナル上に取得したデータが表示される

~~省略~~
[
  {
    id: 'a',
    data: {
      email: 'b@gmail.com'
    }
  },
  {
    id: 'c',
    data: {
      email: 'd@gmail.com'
    }
  }
]

またindex.vueのasyncData内で取得したデータを受け取っているため
http://localhost:3000/にアクセスすると下画像のように取得したデータが表示される

まとめ

今回はサーバーサイドでfirebaseのデータを取得する方法をまとめました。
ここではfirebase-adminを使った方法でサーバーサイドからデータを取得しました。
秘密鍵を生成しないといけないなどちょっと面倒なのでもっと良い方法があるとおもうんですけどね~
ここまで読んでくれてありがとう

コメント

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