Firebaseの公式リファレンスを見ると以下のように書いてありました。
Firebase Hostingでは、Cloud Functionsを使用してサーバー側の処理を実行できます。つまり、Firebase Hostingサイトのコンテンツの動的生成をサポートすることができます。
どうやらFirebase Hostingは、動的なサイトも運用できるという、何とも素晴らしいサービスのようなので、実際に動的コンテンツの配信を行ってみました。
Node.jsの「Express」を使います。テンプレートエンジンには「EJS」を使います。(ここは別に「Pug」でも「handlebars」でも問題ないです)
Firebase Hostingの環境構築の説明は割愛します。
詳しくはこちらをどうぞ
プロジェクトの階層
階層はこんな感じです。
├── firebase.json
├── functions
│ ├── firebase-debug.log
│ ├── index.js
│ ├── node_modules
│ │ ├── @types
│ │ ├── ・・・・・・・
│ │ ├── ・・・・・・・
│ ├── package-lock.json
│ ├── package.json
│ └── views
│ └── template.ejs
└── public
└── index.html
#npmで「Express」と「EJS」をインストール
expressのインストール
$ npm install express --save
ejsのインストール
$ npm install ejs --save
それぞれnpmでインストールする必要があります。
firebase.json ファイル
firebase.json
を開き、内容を次のコードに置き換えます。
{
"hosting": {
"public": "public",
"rewrites": [
{
"source": "/**",
"function": "sample"
}
]
}
}
関数にHostingリクエストを送信する
リライトルールを使用すると、特定のパターンに一致するリクエストを単一の宛先に送ることができます。
/**
のすべてのリクエストを送信して sample
というHTTPS関数を実行します。
今回はトップページは静的なページを表示し、 /**
にアクセスがあると(/page1と/page2)、 **
に応じた動的コンテンツを返します。
public
ルート直下 /
は「public」ディレクトリ内の index.html
ファイルがロードされます。
ファイルの中身は以下です。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="content-type">
<title>トップページ</title>
<style>
h1 { font-size:18px; background-color:#eaeaea; }
</style>
</head>
<body>
<header>
<h1 id="h1">トップページ</h1>
</header>
<div>
<p>これはトップページです。</p>
<ul>
<li><a href="/page1">ページ1へ</a></li>
<li><a href="/page2">ページ2へ</a></li>
</ul>
</div>
</body>
</html>
HTTPS関数を作成する index.js
/functions/index.js
を開き、内容を次のコードに置き換えます。前述した sample
というHTTPS 関数をここで作成。
const functions = require('firebase-functions');
const express = require('express');
const url = require('url');
const app = express();
const routes = {
"/page1":{
"title":"ページ1",
"content":"これはページ1です。"
},
"/page2":{
"title":"ページ2",
"content":"これはページ2です。"
}
};
app.set("view engine","ejs");
app.engine('ejs', require('ejs').__express);
app.get("*",function(req,res){
const url_parts = url.parse(req.path);
if(routes[url_parts.pathname] == null){
res.status(404).send("page not found");
}else{
res.status(200).render("template",
{
title: routes[url_parts.pathname].title,
content: routes[url_parts.pathname].content
}
);
}
});
exports.sample = functions.https.onRequest(app);
ビューエンジンにejsをセットする
以下の部分です。
app.set("view engine","ejs");
app.engine('ejs', require('ejs').__express);
routesにデータをまとめておく
各ページのデータを routes
変数にまとめています。
アクセス先のpathをキーとして用意し、そのpathで表示されるページ情報を連想配列で値にセットします。
この routes
変数から、パスごとに必要な情報をロードして処理します。
const routes = {
"/page1":{
"title":"ページ1",
"content":"これはページ1です。"
},
"/page2":{
"title":"ページ2",
"content":"これはページ2です。"
}
};
「url」オブジェクトをロードする
「url」オブジェクトをロードします。URLの文字列をパースする為に使います。
const url = require('url');
アクセスしてきたURLをパースする
パースして url_parts
変数に変換します。
const url_parts = url.parse(req.path);
判定
アクセスがあったpathを判定して、 routes[url_parts.pathname]
に一致しなければ404を送り、一致すれば、 routes
変数を元にしたデータをJSONで送り、テンプレートをレンダリングします。
if(routes[url_parts.pathname] == null){
res.status(404).send("page not found");
}else{
res.status(200).render("template",
{
title: routes[url_parts.pathname].title,
content: routes[url_parts.pathname].content
}
);
}
テンプレートファイルを用意
「functions」ディレクトリ内に「views」ディレクトリを作成し、その中に template.ejs
ファイルを作成します。ファイルの中身は以下です。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="content-type">
<title><%=title %></title>
<style>
h1 { font-size:18px; background-color:#eaeaea; }
</style>
</head>
<body>
<header>
<h1 id="h1"><%=title %></h1>
</header>
<div>
<p><%-content %></p>
<ul>
<li><a href="/">トップページへ</a></li>
</ul>
</div>
</body>
</html>
デプロイ後の結果
/
は静的なトップページが表示され、/page1
/page2
にアクセスすると動的ページとしてそれぞれのURLに応じたコンテンツが表示されます。
まとめ
Firebase HostingでCloud Functionsを使えば本格的に動的なサイトも運用できるんです。
しかも無料で(制限あり)
Firebaseは制限多いと思ってましたけど、これならAWS Lambdaより気軽に柔軟なアプリケーションが作れる気がします。Firebase Realtime Databaseと組み合わせて。