🚒

SSRで "Could not reach Cloud Firestore backend." エラーが出た時に疑うこと

2021/11/09に公開
1

Firestoreからのデータ取得時にエラーが発生した

Firestoreからデータを取得しようとした際に、以下のようなエラーが出て取得できないことがあります。

@firebase/firestore: Firestore (9.3.0): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds.
This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.

私の場合は

  • Nuxt.js(バージョン3)で開発中
  • SSR: true にした状態でFirestoreのデータをSSRで取得したい(OGP表示やキャッシュに使うため)
  • devモードでは正常にデータを取得できているのに、ビルドしてyarn startすると上記のエラーが表示されてデータが取得できない

という事象でした。もしかしたらNext.jsの getstaticprops などでも似たような現象が発生するかもしれません。

表示されたメッセージによればインターネット接続に異常があると示唆されていますが、もちろんインターネットには繋がっているし、VPN接続なんかもしていません。

試しにこのメッセージ内容で検索してみると、

firebase-toolsの最新版をinstallすれば直るとか、

https://knym.net/firebaseでcould-not-reach-cloud-firestore-backend/

experimentalForceLongPolling: true オプションを設定すればよいとか、

https://github.com/firebase/firebase-js-sdk/issues/2923

firebaseConfigのdatabaseURLを指定していなかったとか、

https://qiita.com/moemon/items/c55824e98fae5276e80e

そういった情報が出てきますが、私の場合はいずれの方法でも解決しませんでした。

(これらの記事の方法で解決した方はおめでとうございます!)

で、いろいろ調べた結果、私のようなケースでは、根本的にこの問題には解決法が存在しないということに気づきました。

というのも、Firebase JS SDKはクライアントサイドでユーザーが接続する際に使うためのものであって、Node.jsのサーバーサイドから利用することはそもそも許容されていないのです。

Firebase JS SDKはクライアントサイドで利用するためのもの

公式ドキュメントを見ても、node.jsからFirestoreに接続する場合は、firebase-adminを使う方法しか案内されていません。

https://firebase.google.com/docs/firestore/quickstart#node.js

GitHubのSDKページにはより直接的に明言されています。

https://github.com/firebase/firebase-js-sdk

This SDK is intended for end-user client access from environments such as the Web, mobile Web (e.g. React Native, Ionic), Node.js desktop (e.g. Electron), or IoT devices running Node.js. If you are instead interested in using a Node.js SDK which grants you admin access from a privileged environment (like a server), you should use the Firebase Admin Node.js SDK.

こう言われてみれば確かに当たり前のことで、そもそも許容されていない使い方をしているのですから、エラーになって当たり前だったのです。

解決策

ではどうすれば良いのかというと、サーバーサイドでデータを取得する際は firebase-admin を使いましょう。それだけです。

Nuxt.jsやNext.jsのようなフレームワークで、「初期描画ではサーバーサイド、追加読み込み/ページ遷移ではクライアントサイドでデータ取得」という場合は、Nuxt/Next内にAPIサーバーを作って、サーバーからもクライアントからも同じエンドポイントを叩くようにするのが一番楽だと思います。

Next.jsならAPI Routes

https://nextjs.org/docs/api-routes/introduction

Nuxt 3.xならServer Directory

https://v3.nuxtjs.org/docs/directory-structure/server/

Nuxt 2.xならServer Middleware

https://nuxtjs.org/docs/configuration-glossary/configuration-servermiddleware/

と、それぞれでAPIサーバーを簡単に作成する機能が提供されています。

もしくは当然ですが、SPAにして全部クライアントサイド取得に変えればこの問題は起きません。

firebase-adminはfirebaseと違ってセキュリティルールが無視されるので、その権限制御は自前で行う必要がありますが、一部の人しか見えてはいけないデータはそもそもSSRする必要が薄いので、権限制御をFirebaseに寄せてSPAで使う方が正しいと思います。

私の場合は単に静的メディアサイトのデータベースをfirestoreにするだけのREST API的な使い方を想定していて、権限制御を考える必要は特になかったのでAPIサーバーを噛ませる形で解決しました。

なぜ気づかなかったのか

なぜこのような初歩的な見落としをしてしまったのかについて一応自分なりに考えてみたところ、

「最初からサーバーサイドでFirestoreを使うと決めていないと、firebase-adminを使うという発想に辿り着きにくい」構造になっているからではないかと思いました。

実のところ、「Firestore SSR」とか「Firestore getStaticProps」とかで検索すると、firebase-adminを使いましょうという記事は出てきます。

https://qiita.com/centerfield77/items/799f2840f48221c2f84d

https://fetchkun.com/entry/nuxt-firestore-ssr/

しかし、これらは「最初からfirestoreをサーバーサイドで取得しようと思っていなかった」、または「そもそもサーバーサイドとクライアントサイドでFirestoreの取得方法が異なるという意識がない」場合は辿り着くのが不可能です。

特にNuxt.jsのようなユニバーサルフレームワークだと、SPAとSSRで書くコードもほぼ同じ、かつサーバーサイドとクライアントサイドの両方で同じリクエストを行うので、SPAとSSRで取得方法が異なるという意識がありませんでした。

Firebase を JavaScript プロジェクトに追加する」というJS SDKの説明ページだけを見ても、直接的にサーバーサイドで使えないという警告があるわけではないので、ブラウザ専用なのだと気づきにくくなっています。

これは、上記のページは「サーバー・アプリ・ウェブのどれで使うか」を選択した後のスタートガイドなので、サーバーサイドでの使い方はサーバー用のページを見る前提になっているからでしょう。

そして何よりも、このエラーメッセージに対してこの回答を提示しているサイトが日英問わずほとんど出てこない問題があります。

セキュリティルールが例えば allow read; if request.auth != null; になっていれば、サーバーサイドからのリクエストでは認証ユーザーがいないのでエラーになる、ということにまだ気づきやすいのですが、
セキュリティルールを設定していない場合だと、表示されるメッセージがサーバーサイドかどうかと関係あるようには全く見えない Could not reach Cloud Firestore backend なのです。

できればこのエラーメッセージが改善されることを期待しつつ、とりあえずこのメッセージで検索した際にこういう記事が出てきたら嬉しい方がいるのではないかと思い、書いてみることにしました。

どなたかの参考になれば幸いです。

Discussion