FirebaseにおけるCloud Functions
Cloud Functionsはアクセスが有ったときだけ動くサーバのようなものだと勝手に解釈しています。通常のサーバと違い、稼働しっぱなしではなく機能が呼ばれたときだけ課金の対象になる点がとてもユニークです。
サーバから実行されるので、厳重に管理しなければならない処理はCloud Functionsにおまかせです。例えばそうですね、Nipoの例であれば、
- クレジットカード決済
- MailGunによる新着日報お知らせメールの送信
- Algoliaのアクセス制限付きキーコードの発行(全文検索に利用)
などをクラウドファンクションで実行しています。
見逃しちゃいけないポイント!Cloud Functionsはセキュリティルールを素通りできます
クライアントから実行するとセキュリティルールの制限を受けますが、セキュリティルールを通り越して処理をしたい場合もクラウドファンクション経由で処理をすることでセキュリティルールをなかったものとしてデータのアクセスができるようになります。
Cloud Functionsはindex.tsに書きましょう
そんな便利なクラウドファンクションは、index.tsに処理を記述していきます。記述するとこんな感じ
import * as functions from 'firebase-functions' const admin = require('firebase-admin') const firebaseKey = { 'type': 'service_account', 'project_id': 'さんぷる', 'private_key_id': '長い桁の鍵', 'private_key': '長い桁の鍵', 'client_email': 'さんぷる', 'client_id': 'さんぷる', 'auth_uri': 'さんぷる', 'token_uri': 'さんぷる', 'auth_provider_x509_cert_url': 'さんぷる', 'client_x509_cert_url': 'さんぷる' } admin.initializeApp({ credential: admin.credential.cert(firebaseKey), databaseURL: 'https://salonkarte-87b6a.firebaseio.com' }) // ↑ここまではほぼ定型文 // 例えばデータが追加されたらカウントアップするサンプルクラウドファンクション // カウンターみたいな処理もクラウドファンクションに任せたほうがいいかも exports.addDataCounter = functions.firestore.document('user/{userId}/data/{dataId}').onCreate(async (snap, context) => { const userId = context.params.userId const ref = await admin.firestore().collection('user').doc(userId).collection('lock').doc('state').get() if (ref.exists) { let currentCnt = ref.data().dataCnt currentCnt = currentCnt + 1 admin.firestore().collection('user').doc(userId).collection('lock').doc('state').update({ dataCnt: currentCnt}) } else { admin.firestore().collection('user').doc(userId).collection('lock').doc('state').set({ dataCnt: 1}) } })
上は簡単なクラウドファンクションのサンプルコードです(TypeScript)
addDataCounterの行が実際の処理となる部分です。このサンプルはデータが書き込まれたらカウントと+1するというものです。
クラウドファンクションのファイルを関数単位で分けよう
普通に考えれば1つのファイルにすべての処理を書くのは得策ではありませんが、公式サイトにはなぜかクラウドファンクションのファイルを分割する方法が書かれていません。Nipoの開発を始めた当時はこの辺の情報が結構少なく、さらにクラウドファンクション自体がβ版でした。(今は正式版です)
さて、クラウドファンクションの関数を1つのファイルに切り出してみましょう。注意点としては、
admin.initializeApp
この処理は1回しか呼び出せません。複数回呼び出すとエラーで落ちます。
先程のサンプルコードを例に、addDataCounterを別のファイル(myCounter.ts)に切り分けてみましょう。
【index.ts】 import * as functions from 'firebase-functions' import * as myCounter from './myCounter' // 追記 const admin = require('firebase-admin') const firebaseKey = { 'type': 'service_account', 'project_id': 'さんぷる', 'private_key_id': '長い桁の鍵', 'private_key': '長い桁の鍵', 'client_email': 'さんぷる', 'client_id': 'さんぷる', 'auth_uri': 'さんぷる', 'token_uri': 'さんぷる', 'auth_provider_x509_cert_url': 'さんぷる', 'client_x509_cert_url': 'さんぷる' } admin.initializeApp({ credential: admin.credential.cert(firebaseKey), databaseURL: 'https://salonkarte-87b6a.firebaseio.com' }) // ↑ここまではほぼ定型文 // ↓ここが追記されます module.exports = { myCounter }
続いて分割された側のファイルコードはこちら
【myCounter.ts】 import * as functions from 'firebase-functions' import * as admin from 'firebase-admin' // 例えばデータが追加されたらカウントアップするサンプルクラウドファンクション // カウンターみたいな処理もクラウドファンクションに任せたほうがいいかも export default functions.firestore.document('user/{userId}/data/{dataId}').onCreate(async (snap, context) => { const userId = context.params.userId const ref = await admin.firestore().collection('user').doc(userId).collection('lock').doc('state').get() if (ref.exists) { let currentCnt = ref.data().dataCnt currentCnt = currentCnt + 1 admin.firestore().collection('user').doc(userId).collection('lock').doc('state').update({ dataCnt: currentCnt}) } else { admin.firestore().collection('user').doc(userId).collection('lock').doc('state').set({ dataCnt: 1}) } })
このように無事ファイルを分けることができました。めでたしめでたし。
コメント