概要
こちらの記事を購入して初めてCloud Functionsについて学んだ内容を備忘録として残しています
Cloud Functions for Firebaseとは
Cloud Functions for Firebase はサーバーレス フレームワークで、Firebase の機能と HTTPS リクエストによってトリガーされたイベントに応じて、バックエンド コードを自動的に実行できます。JavaScript または TypeScript コードは Google のクラウドに保存され、マネージド環境で実行されます。独自のサーバーを管理およびスケーリングする必要はありません。
環境構築
プロジェクトの作成
Cloud Firestore
の作成
Firebase CLI導入
npm install -g firebase-tools
プロジェクトディレクトリを作成
# 任意のプロジェクトディレクトリを作成
mkdir functions-test
cd functions-test
# プロジェクトを作成したGoogleアカウントでログイン
firebase login
Firebaseプロジェクトを初期化
firebase init
######## #### ######## ######## ######## ### ###### ########
## ## ## ## ## ## ## ## ## ## ##
###### ## ######## ###### ######## ######### ###### ######
## ## ## ## ## ## ## ## ## ## ##
## #### ## ## ######## ######## ## ## ###### ########
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices.
( ) Database: Configure Firebase Realtime Database and deploy rules
(*) Firestore: Deploy rules and create indexes for Firestore
(*) Functions: Configure and deploy Cloud Functions
( ) Hosting: Configure and deploy Firebase Hosting sites
>(*) Storage: Deploy Cloud Storage security rules
( ) Emulators: Set up local emulators for Firebase features
( ) Remote Config: Get, deploy, and rollback configurations for Remote Config
? Please select an option: (Use arrow keys)
> Use an existing project
Select a default Firebase project for this directory:
Firestore Setup
? What file should be used for Firestore Rules? (firestore.rules)
? What file should be used for Firestore indexes? (firestore.indexes.json)
? What language would you like to use to write Cloud Functions?
JavaScript
> TypeScript
? Do you want to use ESLint to catch probable bugs and enforce style? (Y/n)
# 今回はESLintは導入しないので"n"
=== Storage Setup
? What file should be used for Storage Rules? (storage.rules)
+ Firebase initialization complete!
# コードを最新にする
npm install firebase-admin@latest firebase-functions@latest
npm install -g firebase-tools
403エラーが発生する場合は以下を参考にしてください
npmのアップデートを検証する
# アップデート検証ツールをインストール
npm install -g npm-check-updates
# 検証開始
ncu
# package.jsonをアップデート
ncu -u
# 最新バージョンをインストール
npm install
Cloud Functionsをデプロイする
サンプルメソッドのコメントアウトを解除する
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) => {
// FirebaseConsole(GUIの管理画面)のFunctionsにログを出力
functions.logger.info("Hello logs!", { structuredData: true });
// response.sendでリクエスト元に値を返す
response.send("Hello from Firebase!");
});
Cloud Functions のデプロイをする
作成したプロジェクトを従量制に変更してください
firebase deploy --only functions
デプロイ結果
Firebase Local Emulator Suiteの導入
Firebase Local Emulator Suite は、Cloud Firestore、Realtime Database、Cloud Storage、Authentication、Cloud Functions、Pub/Sub、Firebase Hosting を使用してアプリをローカルでビルドおよびテストするデベロッパー向けの高度なツールセットです。
firebase init emulators
=== Emulators Setup
? Which Firebase emulators do you want to set up? Press Space to select emulators, then Enter to confirm your choices.
(*) Authentication Emulator
(*) Functions Emulator
>(*) Firestore Emulator
( ) Database Emulator
( ) Hosting Emulator
( ) Pub/Sub Emulator
(*) Storage Emulator
# -------------------------------------------------------------------------
# 上記のように選択してEnter
# -------------------------------------------------------------------------
? Which port do you want to use for the auth emulator? (9099)
? Which port do you want to use for the functions emulator? (5001)
? Which port do you want to use for the firestore emulator? (8080)
? Which port do you want to use for the storage emulator? 9199
# -------------------------------------------------------------------------
? Would you like to enable the Emulator UI? Yes
? Which port do you want to use for the Emulator UI (leave empty to use any available port)?
? Would you like to download the emulators now? No
+ Firebase initialization complete!
ビルドする
cd functions/
npm run build
エミュレーターを起動
firebase emulators:start
┌─────────────────────────────────────────────────────────────┐
│ ✔ All emulators ready! It is now safe to connect your app. │
│ i View Emulator UI at http://localhost:4000 │
└─────────────────────────────────────────────────────────────┘
┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator │ Host:Port │ View in Emulator UI │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ localhost:9099 │ http://localhost:4000/auth │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Functions │ localhost:5001 │ http://localhost:4000/functions │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore │ localhost:8080 │ http://localhost:4000/firestore │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Storage │ localhost:9199 │ http://localhost:4000/storage │
└────────────────┴────────────────┴─────────────────────────────────┘
Emulator Hub running at localhost:4400
Other reserved ports: 4500
Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
Cloud Firestoreにエクスポート・インポートする
保存(エクスポート)
firebase emulators:export ed
値をエミュレータに読み込む(インポート)
firebase emulators:start --import=ed
HTTPリクエストをトリガーに Cloud Firestore の値を返す
Cloud Firestore
Google の柔軟でスケーラブルな NoSQL クラウド データベースを使用して、クライアント側開発とサーバー側開発のデータを保存、同期します。
Cloud Firestore にデータを追加する
Cloud Firestoreからデータを返却する処理を追加
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
// firebase-adminを初期化
admin.initializeApp();
export const getBooks = functions.https.onRequest(async (request, response) => {
try {
// firestoreのインスタンスを取得
const db = admin.firestore();
//インスタンスからコレクションを取得
const ref = await db.collection('books').get();
response.send(ref.docs.map(book => book.data()));
} catch (e) {
console.error(e);
response.status(500).send(e);
}
});
ビルドする
npm run build
エミュレータ上のFirestoreにデータを追加
firebase emulators:start
# 8080ポートが既に使用されているエラーが出る場合
firestore: Port 8080 is not open on localhost, could not start Firestore Emulator.
⚠ firestore: To select a different host/port, specify that host/port in a firebase.json config file:
{
// ...
"emulators": {
"firestore": {
"host": "HOST",
"port": "PORT"
}
}
}
i emulators: Shutting down emulators.
Error: Could not start Firestore Emulator, port taken.
# 8080ポートが何に使われているかを確認
sudo lsof -i:8080
java 4190 xxxxx 147u IPv6 0x372f84f782df4f69 0t0 TCP localhost:http-alt (LISTEN)
# プロセス終了
kill 4190
# 再度エミュレーターを起動する
firebase emulators:start
✔ functions[us-central1-getBooks]: http function initialized (http://localhost:5001/functions-test-6696a/us-central1/getBooks).
┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator │ Host:Port │ View in Emulator UI │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ localhost:9099 │ http://localhost:4001/auth │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Functions │ localhost:5001 │ http://localhost:4001/functions │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore │ localhost:8080 │ http://localhost:4001/firestore │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Storage │ localhost:9199 │ http://localhost:4001/storage │
└────────────────┴────────────────┴─────────────────────────────────┘
Firestoreにアクセスしコレクションを追加する
Cloud Firestoreからデータを返却する処理を実行する
curl http://localhost:5001/functions-test-6696a/us-central1/getBooks
[{"ins_ts":{"_seconds":1640519638,"_nanoseconds":801000000},"title":"CloudFunctions入門","price":1000}]%
Cloud Firestore トリガー
Cloud Functions を使用すると、クライアント コードを更新することなく、Cloud Firestore 内のイベントを処理できます。Cloud Firestore の変更は、DocumentSnapshot インターフェースまたは Admin SDK を使用して行うことができます。
イベントタイプ トリガー
onCreate
ドキュメントが最初に書き込まれたときにトリガーされます。
onUpdate
すでに存在するドキュメントの値が変更されたときにトリガーされます。
onDelete
データを含むドキュメントが削除されたときにトリガーされます。
onWrite
onCreate
、onUpdate
またはonDelete
がトリガーされたときにトリガーされます。
onCreate
// 以下を追加する
export const store = functions.https.onRequest(async (request, response) => {
if (request.method !== 'POST') {
response.status(400).send('【不正】リクエストタイプが不正です。');
}
const body = request.body;
try {
const db = admin.firestore()
await db.collection('books').add({body})
response.send("Complete")
} catch (e) {
console.error(e);
response.status(500).send(e)
}
});
export const onCreateByBook = functions.firestore.document('books/{isbn}').onCreate(async (snapshot, context) => {
const storedData = snapshot.data();
const isbn = context.params.isbn
const title = storedData.body.title
const price = storedData.body.price
console.log(`【新着本追加】ISBN: ${isbn}, タイトル: ${title}, 価格: ${price}`);
}
)
ビルド
npm run build
エミュレーターを起動する
firebase emulators:start
i emulators: Starting emulators: auth, functions, firestore, storage
⚠ functions: The following emulators are not running, calls to these services from the Functions emulator will affect production: database, hosting, pubsub
⚠ Your requested "node" version "14" doesn't match your global version "12"
i firestore: Firestore Emulator logging to firestore-debug.log
⚠ ui: Emulator UI unable to start on port 4000, starting on 4001 instead.
i ui: Emulator UI logging to ui-debug.log
i functions: Watching "/Users/habanaoki/Documents/study/functions-test/functions" for Cloud Functions...
✔ functions[us-central1-getBooks]: http function initialized (http://localhost:5001/functions-test-6696a/us-central1/getBooks).
✔ functions[us-central1-store]: http function initialized (http://localhost:5001/functions-test-6696a/us-central1/store).
✔ functions[us-central1-onCreateByBook]: firestore function initialized.
┌─────────────────────────────────────────────────────────────┐
│ ✔ All emulators ready! It is now safe to connect your app. │
│ i View Emulator UI at http://localhost:4001 │
└─────────────────────────────────────────────────────────────┘
┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator │ Host:Port │ View in Emulator UI │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ localhost:9099 │ http://localhost:4001/auth │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Functions │ localhost:5001 │ http://localhost:4001/functions │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore │ localhost:8080 │ http://localhost:4001/firestore │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Storage │ localhost:9199 │ http://localhost:4001/storage │
└────────────────┴────────────────┴─────────────────────────────────┘
Emulator Hub running at localhost:4400
Other reserved ports: 4500
Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
登録処理
curl -X POST -H "Content-Type: application/json" -d '{"price":500,"title":"sample"}' http://localhost:5001/functions-test-6696a/us-central1/store
Firestore・FunctionsのURLにアクセスする
Firestoreに登録したデータが表示される
onUpdate
document()で指定したコレクションのドキュメントの更新をトリガーに関数が実行される。
// 以下を追加後ビルドします
export const onUpdateByBook = functions.firestore.document('books/{isbn}').onUpdate(async (change, context) => {
const before = change.before.data()
const after = change.after.data()
console.log(`【変更前】: ${before.body.price}, [変更後]: ${after.body.price}`);
}
)
FireStoreから任意のコレクションを更新します
更新処理をトリガーできれば成功
onDelete
document()で指定したコレクションからのドキュメントの削除をトリガーに関数が実行される。
// 以下を追加後ビルドします
exports.onDeleteByBook = functions.firestore.document('books/{isbn}').onDelete(async (snapshot, context) => {
const data = snapshot.data()
const isbn = context.params.isbn
const title = data.body.title
const price = data.body.price
console.log(`【削除】ISBN: ${isbn}, タイトル: ${title}, 価格: ${price}`);
})