コード日進月歩

しんくうの技術的な小話、メモ、つれづれ、など

Firebase Hosting + Cloud Functions for Firebase を下地に Express.js で Basic認証しつつ静的コンテンツを返すサーバーを作る

Qiitaであがってるような奴をちゃんとした書き方でやる。

環境

今回紹介する方法は要Firebase CLIなのでインストールする。

$ node -v
v11.15.0

$ firebase --version
7.11.0

やること

  1. プロジェクトを作る
  2. Cloud FunctionsでExpress.jsのコードを書く
  3. Cloud Functionを実行するようにHosting側を整理する
  4. 表示したいhtmlを置く。
  5. deploy

プロジェクトを作る

作業するディレクトリで以下のコマンドでプロジェクトを作る

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. (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◯ Database: Deploy Firebase Realtime Database 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

のようにどの機能を使うか聞かれるので Hosting , Functions を指定する。また利用するのはjavascriptにすること。

Cloud FunctionsでExpress.jsのコードを書く

Hostingで配信するためのディレクトリ(デフォルトは public)と CloudFunction用のディレクトリである function ができるので、その中の index.js で Express.jsとBasic認証用のpackageをインストールする。

$ cd functions/
$ npm install express
$ npm install basic-auth-connect

これで、package.jsonにも記述されて使える状態になったので index.js 側に以下のように記述する。

// モジュールのロードおよびインスタンスに代入
const functions = require('firebase-functions');
const express = require('express');
const basicAuth = require('basic-auth-connect');
const app = express();

// Basic認証のデータを定義
const loginUserName = "user";
const loginPassword = "password";

// すべてのリクエスト(app.all)かつすべてのパス('/*') に対してBasic認証をかける
// 特定のパスに関してかける場合は /* を /hoge/* などにかえる
app.all('/*', basicAuth(function(user, password) {
    return user === loginUserName && password === loginPassword;
}));

// 静的配信をするファイルのパスを指定する。今回はstaticを指定。
app.use(express.static(__dirname + '/static/'));

// node,jsのexportsで、app関数として呼び出せるように設定
exports.app = functions.https.onRequest(app);

Cloud Functionを実行するようにHosting側を整理する

firebase.jsonの追記

Hostingの設定で、functionを呼び出すようにrewriteの記述をふやします。

    "rewrites": [
      {
        "source": "**",
        "function": "app"
      }
    ],

こちらですべてのパスでindex.jsのappアプリが実行されるようになる。ただし、Firebaseはデフォルトとして以下のルールでファイルのレスポンスを決めているため、もしpublic側にファイルがある場合はそれが優先される

1. /__/* パスセグメントで始まる予約済みの名前空間
2. リダイレクトの構成
3. 正確に一致する静的コンテンツ
4. リライトの構成
5. カスタムの 404 ページ
6. デフォルトの 404 ページ

ホスティング動作を構成する | Firebase - Hosting のレスポンスの優先順位

静的ファイルの削除

前述の通り、 public/index.html があるとそちらが優先されてレンダリングされてしまうのでpublic配下のファイルを削除する。

表示したいhtmlを置く

Express.jsにて functions/static 配下のページをレンダリングするように設定しているので、こちらに表示させたいファイルを置く。

deploy

以下のコマンドでアップロードする。

$ firebase deploy

デプロイが完了するとルートのURLが表示されるのでそちらで動作確認する。

トラブルシュート

firebase deployでエラーになる

だいたい index.js の書き方がおかしかったり、firebase.jsonの記述がおかしかったりするのでそちらを確かめる

いきなりエラーページを返すようになった

Cloud Functionの制限に引っかかっている可能性があるので、エラーメッセージを確かめる。

Error: quota exceeded (Quota exceeded for quota group 'FunctionCallsNonbillable' and limit 'Function invocations per 100 seconds' of service 'cloudfunctions.googleapis.com' for consumer 'project_number:0000000000'.); to increase quotas, enable billing in your project at https://console.cloud.google.com/billing?project=hogehoge. Function cannot be executed.

のような記述があれば処理の上限に至ってたりするので、Basic認証に通す対象のファイルの内容などを精査する必要がある。

詳しくは https://cloud.google.com/functions/quotas?hl=ja

参考/関連リンク