Node.jsを使ってFirestoreを監視し、ローカルファイルを書き換える

3 min読了の目安(約3200字TECH技術記事

Raspberry Pi や Jetson などのマイクロコンピュータを使って何かをするときに、インターネット経由で外から設定値を変えたいことってよくありますよね?(ない)

そんなときに使えるのがGoogleのFirebaseが提供するリアルタイムデータベース「Cloud Firestore」です。数行のコードでリアルタイムにデータベースの更新を受け取ることができます。

データを更新するコンソールがFirebase内に用意されているので、更新はそれでやってしまえば、Firestoreの更新の監視をするコードだけでインターネット経由で設定値を変える仕組みが作れます。

今回は、Node.jsでローカルファイルを書き換える方法でやってみる方法を簡単に紹介します。

事前準備

Firebaseにプロジェクトを作って、Cloud Firestoreのデータベースを作成してください。日本で実行するならリージョンは「asia-northeast1」とかで良いと思います。

その後、Cloud Firestoreにコレクション、ドキュメント、フィールドを追加します。今回はサンプルで以下のように作りました。

セキュリティールールは以下のようにしておくと良いと思います。

service cloud.firestore {
 match /databases/{database}/documents {
   match /{document=**} {
     allow read, write: if request.auth.uid != null;
   }
 }
}

Firebaseの秘密鍵を使ってアクセスするので、プロジェクトの設定の「ユーザーと権限>サービスアカウント>Firebase Admin SDK」の新しい秘密鍵の生成をして、ダウンロードしたJsonファイルをNode.jsのプロジェクト直下などに置いてください。

package.json

コマンドライン実行の引数を簡単に扱えるcommanderとFirestoreを使うためにfirebase-adminを使います。

{
 "name": "remote-sttings",
 "description": "Listen to a firestore collection and update a local setting file.",
 "main": "watch.js",
 "scripts": {
   "test": "echo \"Error: no test specified\" && exit 1"
 },
 "dependencies": {
   "commander": "^2.20.0",
   "firebase-admin": "^8.2.0"
 }
}

watch.js

#!/usr/bin/env node

const program = require('commander');
const fs = require('fs');

const credentialPath = './serviceAccountKey.json'
let outputFilePath = './output/settings.json';

program
 .option('-o, --out [path]', 'A path to an output file.')
 .parse(process.argv);

if(program.out) outputFilePath = program.out;

const admin = require('firebase-admin');
const serviceAccount = require(credentialPath);

// Firebaseの初期化
admin.initializeApp({
 credential: admin.credential.cert(serviceAccount)
})

const db = admin.firestore()

console.log('[INFO] Start listen to Firestore.')

// Firebaseのドキュメントを購読
const unsub = db.collection('settings').doc('test').onSnapshot(snapshot => {
 console.log('[INFO] Firestore settings are changed.')
 console.log(snapshot.data())

 // JSONを文字列に変換
 data = JSON.stringify(snapshot.data(), null, 2)

 // ローカルファイルに書き出し
 fs.writeFile(outputFilePath, data, 'utf8', (error) => {
   if (error) {
     console.error(`[ERROR] Can't update ${outputFilePath}.`);
     console.error(`[ERROR] ${error}`)
     process.exit(1)
   } else {
     console.log(`[INFO] ${outputFilePath} was updated.`)
   }
 })
}, error => {
 console.error(`[ERROR] Can't observe firestore document.`)
 console.error(`[ERROR] ${error}`)
 process.exit(1)
})

// Node.jsのプロセスを待機させる
const reader = require('readline').createInterface({
 input: process.stdin,
 output: process.stdout
});

reader.on('close', function () {
 unsub()
 console.log('[EXIT]');
});

あとはコマンドラインで実行すれば、Firestoreの値が変更されるたびにローカルのファイルが書き換わります。

$ node watch.js

料金

使用量と制限  |  Firebase

ここに無料の割当が書かれています。個人的な用途で使う分には無料で使える割り当て量だと思います。

こんな仕組みがServerlessでものの1時間程度で作れてしまうのは本当に便利な世の中です。

今回のサンプルコードはGithubにも上げていますので、参考にしてください。Firebaseのシークレットキーの取り扱いには十分気をつけましょう。

dforest/firestore-nodejs-sample