はじめに
静的なWebアプリをさくっと公開できるFirebaseのHostingは、同じくFirebaseのCloud Functionsと連携させることで動的なWebアプリにすることができて素敵だったので色々遊んでみました。
調べると連携方法がいくつかあったので、それぞれの簡単な実装でパフォーマンス(レスポンスが返ってくるまでの時間)を測定し、現時点でのメリットとデメリットをまとめてみました。
サンプルページ
https://functions-call.firebaseapp.com
それぞれのCloud Functionsの呼び出し方法ごとにレスポンスにかかった時間をみれるだけのWebアプリ
サンプルコード
※下の実装例と細かい変更点はある
要約
現在利用できる方法を自分なりに大きく3パターンに分け、それぞれの特徴をまとめた。
No. | 呼び出し方法 | 認証処理 | 東京リージョン | Firebaseアプリ以外からの呼び出し | CORS対応 |
---|---|---|---|---|---|
1 | Firebaseアプリからのリクエスト | ✅Firebaseの認証 | ✅利用可 | ⛔️ | ✅不要(オリジンサーバとCloud Functions のドメインは異なるが、FirebaseのSDKを利用するため) |
2 | HTTPでのリクエスト(Firebase Hosting連携) | ✴️自前で設定が必要 | ⛔️2019年4月では利用不可 | ✅可能 | ✅不要(オリジンサーバとCloud Functionsのドメインが同じになるため) |
3 | HTTPでのリクエスト(CORS対応のためExpress利用) | ✴️自前で設定が必要 | ✅利用可 | ✅可能 | ✴️自前で設定が必要 |
これらの特徴を踏まえて、どれを選択するかはこんな感じ
- Cloud Functionsを呼び出しはFirebaseのSDKを利用したものだけの想定であれば、1. Firebaseアプリからのリクエストを利用するのが良い
- Cloud Functionsにの呼び出しURLにカスタムドメインを設定したり、Hostingを利用してSSR(サーバサイドレンダリング)を利用したSPAを作りたいのであれば2. HTTPでのリクエスト(Firebase Hosting連携)に限られる
-
2019/05/11現在ではこれが利用できるのは
us-central1
(Iowa)に限られているので、日本からアクセスする場合にレスポンス時間が少し悪くなる
-
2019/05/11現在ではこれが利用できるのは
- 日本からの利用のみでパフォーマンスを求める必要があり、Firebaseアプリ以外からの呼び出しにもCloud Functionsを利用したい場合は東京リージョンを利用するために3. HTTPでのリクエスト(CORS対応のためExpress利用) を利用することになる
それぞれの実装例
Firebaseアプリからのリクエスト
Cloud Functions側のコード
functions.https.onCallを利用する。
import * as functions from 'firebase-functions';
export onCallFromTokyo = functions
.region("asia-northeast1") // デフォルトでは東京リージョン指定できないため、.region("リージョン名")で指定
.https
.onCall((data, context) => {
const name = data.name;
return {
message: "Hello, " + name + ". onCallFrom 🗼"
}
});
Hosting側のコード
firebase.functions.Functions.httpscallableを利用する。
import firebase from 'firebase/app';
import 'firebase/functions';
const REGION_TOKYO = "asia-northeast1";
/* ~~~省略~~~ */
private onCall = async (): Promise<string> => {
// Firebaseの
const onCallFromTokyo = firebase.app()
.functions(REGION_TOKYO) // Functionsのデプロイされているregion指定
.httpsCallable("onCallFromTokyo"); // Functionsの関数名指定
try {
const result = await onCallFromTokyo({name: "sugi"});
return result.data.message;
} catch(e) {
throw e;
}
};
HTTPでのリクエスト(Firebase Hosting連携)
Cloud Functions側のコード
functions.https.onRequestを利用する。
一番シンプルなCloud Functionsの実装方法で、region指定しないとus-central1
になる。
import * as functions from 'firebase-functions';
export onRequestFrom = functions.https.onRequest((req, res) => {
res.send({message: "Hello, " + req.query.name + ". onRequestFrom 🇺🇸"})
});
firebase.jsonの設定
"hosting"->"rewrites"に、URLとアクセスリソースへのマッピングを定義する。
Hostingのみの利用でSPAであれば、firebase init --hosting
で作成された状態が、https://<YOUR_DOMAINE>
へのリクエストはパスがなんであれindex.html
へのアクセスになる設定がされている。
{
"source": "**", // ここより上に設定されているrewriteルールにマッチしないどんなパスでも
"destination": "/index.html" // このリソースを表示する
}
sources
に対してfunction
というキーワードを使うことで、Hostingのドメインを利用してCloud Functionsの呼び出しURLをマッピングさせることができる。
**省略**
"hosting": {
**省略**
"rewrites": [
{
"source": "/onRequestWithRewrite", // このパスの場合
"function": "onRequestFromUS" // この名前のCloud Functionsを呼び出す
},
{
"source": "**",
"destination": "/index.html"
}
]
}
**省略**
Hosting側のコード
通常のHTTPリクエストであるためaxiosを利用。
import axios from "axios";
/* ~~~省略~~~ */
private onRequestWithRewrite = async (): Promise<string> => {
try {
// 同じドメインのパスへリクエストを送るため、axiosのGETであればパスの指定だけで良い
const response = await axios.get("/onRequestWithRewrite?name=sugi");
return response.data.message;
} catch(e) {
throw e;
}
};
HTTPでのリクエスト(CORS対応のためExpress利用)
Cloud Functions側のコード
functions.https.onRequestを利用するが、HostingしているオリジンサーバとCloud Functionsのドメインは異なるため、CORS設定が必要になる。
そのため公式ドキュメントに従い、Expressを利用して実装する。
import * as functions from "firebase-functions";
import * as express from 'express';
import * as cors from 'cors';
const REGION_TOKYO = "asia-northeast1";
// Hostingから呼び出すため、CORS設定が必要になるので必要なモジュールを準備
const app = express();
app.use(cors({})); // どのサイトからでも呼び出せるガバガバ設定
// Expressを利用してAPIの処理を実装
app.get("/", (req, res) => { // リクエストパスを設定
res.send({message: "Hello, " + req.query.name + ". onRequestFrom 🗼"})
});
onRequestFromTokyo = functions // ここでhttps://<DOMAIN>/onRequestFromTokyo/がエンドポイントとなる
.region(REGION_TOKYO) // 東京リージョン指定
.https.onRequest(app); // onRequestにExpressアプリを渡す
export default onRequestFromTokyo;
Hosting側のコード
通常のHTTPリクエストであるためaxiosを利用。
import axios from "axios";
const REGION_TOKYO = "asia-northeast1";
const PROJECTID = "functions-call";
const FUNCTIONS_DOMAIN_TOKYO = REGION_TOKYO + "-" + PROJECTID + ".cloudfunctions.net";
/* ~~~省略~~~ */
private onRequest = async (): Promise<string> => {
try {
// デプロイしたFunctionsのURLを指定してGETリクエスト
const response = await axios.get(
"https://" + FUNCTIONS_DOMAIN_TOKYO + "/onRequestFromTokyo?name=sugi");
return response.data.message;
} catch(e) {
throw e;
}
};