40
29

More than 5 years have passed since last update.

[Firebase] HostingとCloud Functionsの連携方法あれこれ

Posted at

はじめに

静的なWebアプリをさくっと公開できるFirebaseHostingは、同じくFirebaseCloud 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利用) ✴️自前で設定が必要 ✅利用可 ✅可能 ✴️自前で設定が必要

これらの特徴を踏まえて、どれを選択するかはこんな感じ

それぞれの実装例

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;
  }
};

参考資料

40
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
29