Firebase Admin SDKでエミュレータと本物のプロジェクトのFirestoreに同時に接続する
この記事について
あまり無いユースケースかもしれませんが、タイトルの通り「Node.js のfirebase-admin-sdk
で、エミュレータの firestore と本物の Firebase プロジェクトの firestore を同時に扱いたい」ということが先日ありました。
ワークアラウンド的な方法にはなりますが、実現出来たのでその紹介です。
Firebase をすでに使っている人向けの記事であり、Firebase emulator や Admin SDK の説明は省略します。
使用する環境・バージョン
{
"engines": {
"node": "18"
},
"dependencies": {
"firebase-admin": "^11.11.1"
}
}
結論
以下のように、エミュレータに接続したい app の初期化のタイミングで(コード中で)、 FIRESTORE_EMULATOR_HOST
に値をセットすることで同時に接続できます。
const realApp = initializeApp(
{
projectId: 'real-project',
},
'real-app',
)
// 本物のプロジェクトのfirestore
const realFirestore = getFirestore(realApp)
process.env.FIRESTORE_EMULATOR_HOST = '127.0.0.1:8080'
const emuApp = initializeApp(
{
projectId: 'emulator',
},
'emulator-app',
)
// エミュレータのfirestore
const emuFirestore = getFirestore(emuApp)
詳細
複数 app の初期化
まず、複数の app の初期化については以下の公式ドキュメントに記載があります。
initializeApp
は通常引数の config のみを指定して呼び出すことが多い(それが default の app になる)ですが、第 2 引数に app 名を指定することで、複数の app を初期化することが出来ます。
そしてgetFirestore
などは引数無しで呼び出すとデフォルトの app に対して firestore を取得しますが、引数に特定の app インスタンス を渡すことでその app に対しての firestore を取得することが出来ます。
// 第2引数のapp名は無しで、configのみで初期化するとデフォルトのappになる
initializeApp(defaultAppConfig)
// getFirestore()などを引数無しで呼び出すと、そのデフォルトのappに対してfirestoreを取得する
const defaultFirestore = getFirestore()
// 第2引数にapp名を指定して、別のconfigとともに初期化することも出来る
const otherApp = initializeApp(otherAppConfig, 'other-app')
// getFirestore()などの引数にappを渡すと、そのappに対してfirestoreを取得できる
const otherFirestore = getFirestore(otherApp)
エミュレータへの接続
そして Firestore のエミュレータに接続したい場合、以下のように環境変数FIRESTORE_EMULATOR_HOST
に値をセットすると勝手に繋ぎにいってくれます。
export FIRESTORE_EMULATOR_HOST="127.0.0.1:8080"
ただし、この環境変数が設定されていると、すべてのgetFirestore()
呼び出しでエミュレータと接続する firestore インスタンスを取得しようとしてしまいます。
片方は本物のプロジェクト、もう片方はエミュレータに接続する方法が無いか調べたところ、全く同じことをやってくれていた issue がありました。
本物のプロジェクトに接続する firestore インスタンスを取得した後に、ランタイムで環境変数を設定してエミュレータに接続する firestore インスタンスを取得するという方法です。
const realApp = initializeApp(
{
projectId: 'real-project',
},
'real-app',
)
// 本物のプロジェクトのfirestore
const realFirestore = getFirestore(realApp)
process.env.FIRESTORE_EMULATOR_HOST = '127.0.0.1:8080'
const emuApp = initializeApp(
{
projectId: 'emulator',
},
'emulator-app',
)
// エミュレータのfirestore
const emuFirestore = getFirestore(emuApp)
注意として、FIRESTORE_EMULATOR_HOST
は app の初期化時ではなくgetFirestore()
の呼び出し時に参照されるようなので、環境変数のセット前にgetFirestore()
まで呼び出す必要があります。
ランタイムで環境変数を書き換えるのでプロダクションコード中では使いたくないですが、テストコードや簡単なスクリプトなどで役に立つかもしれません。
まとめ
そんなに需要があるユースケースでは無い気がしますが、一応本物プロジェクトとエミュレータに同時に接続出来るよという話でした。
試していないですが、firestore 以外のサービスでも出来るかもしれません。
Discussion