Cloud Functions for Firebase (Firebase Functions)で、Firestoreに格納しているデータをJSON形式で返せるか試してみました。
Cloud Functions for Firebaseをデプロイする
Cloud Functions for Firebaseをデプロイするところまでを紹介します。任意のディレクトリで作業します。
firebase init
ウィザードに従って雛形を作成します。
ここでは既存のプロジェクトを選択して、TypeScriptを選択しました。この時点でディレクトリ構造は下図のようになりました。
functions/src/index.ts
をエディタで開いて、helloWorldのコメントを外します。
import * as functions from 'firebase-functions'; // // Start writing Firebase Functions // // https://firebase.google.com/docs/functions/typescript // export const helloWorld = functions.https.onRequest((request, response) => { response.send("Hello from Firebase!"); });
必要なライブラリをインストールして、Firebaseへデプロイします。
cd functions npm install cd .. firebase deploy --only functions
ブラウザで https://us-central1-PROJECT_NAME.cloudfunctions.net/helloWorld
にアクセスすると、Hello from Firebase!
と表示されました。
この記事を書いてから気付いたけど、2年前にもFirebase Functionsに挑戦した痕跡が見つかった。当時からあまり変わってなさそう。
FunctionsでFirestoreのデータを読み出す
つづいて、Cloud FunctionsでFirestoreに格納されているデータを読み出してJSONをレスポンスとして返したいです。
構造は仮に下記のようになっているとします。
Firebase Console上では、データはこのようになっています。
Firebase Admin SDKの初期化に関しては「Add the Firebase Admin SDK to Your Server | Firebase」をみます。アクセスキーを用意してローカルでGOOGLE_APPLICATION_CREDENTIALS
にパスを通しておきます。
export GOOGLE_APPLICATION_CREDENTIALS="/Users/PATH_TO/serviceAccountKey.json"
functions/src/index.ts
に getPrices
でアクセスされた場合のフック*1を追加します。
import * as functions from 'firebase-functions'; import * as admin from 'firebase-admin'; admin.initializeApp({ credential: admin.credential.applicationDefault(), databaseURL: "https://PROJECT_NAME.firebaseio.com" }); export const getPrices = functions.https.onRequest((request, response) => { const ref = admin.firestore().collection('dates/20191231/prices'); ref.get().then( snapshot => { response.json(snapshot.docs); }) .catch(error => { response.status(500).send(error) }); });
https://us-central1-PROJECT_NAME.cloudfunctions.net/getPrices
にアクセスすると、下記のようなJSONが返ってきます。
[ //省略 "_fieldsProto": { "name": { "stringValue": "リーリエ", "valueType": "stringValue" }, "id": { "stringValue": "smp_397", "valueType": "stringValue" }, "price": { "integerValue": "69800", "valueType": "integerValue" }, "postAt": { "timestampValue": { "seconds": "1577772000", "nanos": 0 }, "valueType": "timestampValue" } }, //省略 ]
このままだと使いにくいので、number型は数値で返すようにしたいです。
Firestoreで取得したデータを整えて返す
FirestoreSimple( https://github.com/Kesin11/Firestore-simple )を使うと、より簡単に型付けすることができました。
npm i firestore-simple
でインストールします。
import * as functions from 'firebase-functions'; import * as admin from 'firebase-admin'; import { FirestoreSimple } from 'firestore-simple'; admin.initializeApp({ credential: admin.credential.applicationDefault(), databaseURL: "https://PROJECT_NAME.firebaseio.com" }); export class Card { constructor( public id: string, public name: string, public created: number, ) { } } export const getPrices2 = functions.https.onRequest(async (request, response) => { const firestoreSimple = new FirestoreSimple(admin.firestore()) const dao = firestoreSimple.collection<Card>({ path: `dates/20191231/prices` }) const prices = await dao.fetchAll() response.send(prices) });
デプロイします。
export GOOGLE_APPLICATION_CREDENTIALS="/Users/PATH_TO/serviceAccountKey.json" firebase deploy --only functions
実行すると、下記のようなレスポンスを返します*2。
[ { "id": "smp_245", "price": 100, "name": "イーブイ", "postAt": { "_seconds": 1577799120, "_nanoseconds": 0 } }, { "id": "smp_397", "price": 69800, "postAt": { "_seconds": 1577772000, "_nanoseconds": 0 }, "name": "リーリエ" } ]
Firestoreで取得したデータを並び替えて返す
import * as functions from 'firebase-functions'; import * as admin from 'firebase-admin'; import { FirestoreSimple } from 'firestore-simple'; admin.initializeApp({ credential: admin.credential.applicationDefault(), databaseURL: "https://PROJECT_NAME.firebaseio.com" }); export class Card { constructor( public id: string, public name: string, public price: number, ) { } } function compare(a: Card, b: Card) { if (a.price < b.price) { return 1; } else if (b.price > a.price) { return -1; } else { // 価格が同一だった場合、ID順でソートする const aId = a.id.toLocaleLowerCase const bId = b.id.toLocaleLowerCase if (aId === bId) { return 0 } return bId < aId ? 1 : -1 } } export const getPrices2 = functions.https.onRequest(async (request, response) => { const firestoreSimple = new FirestoreSimple(admin.firestore()) const dao = firestoreSimple.collection<Card>({ path: 'dates/20191231/prices' }) const prices = await dao.fetchAll() response.send(prices.sort(compare)) });
関連記事
- Cloud Functions を TypeScript で書く - Qiita
- Firebase Functionsのデプロイ時にHTTP Error: 400, The request has errorsで失敗する - GAミント至上主義
Cloud FunctionsからFirestoreのデータを取得する方法は下記のモノが役に立ちました。