いわゆるサーバーレス。
TL;DR
すべてのリクエストを Firebase Functions に流して next.js に食わせた結果を返すとSSRになる。最高。
概要
Firebase Hosting は index.html を上げたら動いてくれる静的サイトホスティングだと思っていたが、 全てのルーティングを Firebase functions に全て受けさせる。こともできた
GCP知らない人向けに 一応解説しておくと Firebase Function = Google Cloud Function ≒ AWS Lambda
そんでもって、React で SSR したいとき、スクラッチでもいいけど、一番簡単なのは next.js。next.js 公式にも exmaples があり、読んでみたら勉強になったので解説してみる。
Firebase Hosting の設定
{
"functions": {
"source": "functions"
},
"hosting": {
"public": "public",
"rewrites": [
{
"source": "**",
"function": "next"
}
]
}
}
デフォルトだと public/index.html を返すと指定してある部分を、全部のリクエストを Function に食わせるようにする。
次に、指定された next の Function の設定。
const functions = require('firebase-functions')
const next = require('next')
var dev = process.env.NODE_ENV !== 'production'
var app = next({ dev, conf: { distDir: 'next' } })
var handle = app.getRequestHandler()
exports.next = functions.https.onRequest((req, res) => {
console.log('File: ' + req.originalUrl) // log the page.js file that is being requested
return app.prepare().then(() => handle(req, res))
})
これは Function の定義以外は next.js を 他の node フレームワークに食わせる部分とだいたい一緒。Google Cloud Function の req, res は node の 標準リクエスト、レスポンスオブジェクトとだいたい一緒で(厳密には違う or 互換が明言されてなかった気がするが)、next.js がこれを食えばそのまま動く。
import React from 'react'
export default () => <h1>Hello SSR</h1>
これで SSR しつつクライアントでReactのアプリとして状態を引き継げるわけ。
この example のややこしかった点
app/ と functions/ が別にpackage.json を持ってて別パッケージなんだけど、 app/ で next build
すると、 functions/next にビルドされて、デプロイされた function は functions/index.js から functions/next/*
のビルド結果を読み取って返す。
webpack などと違って外部ライブラリまではビルドされてないので、使うライブラリは両方とも npm install しておく必要があった。ちょっと面倒臭い。(し、ちゃんとスケルトン書けば解決しそうではある)
感想
Cloud Function で HTML 返せば書き味は普通のWebアプリに近くなる。
応答性とかは、やっぱしばらくアクセスなくてスリープしてると遅い。スケールはしそう。
認証もFirebaseに組み込まれてるし、Firestore っていう Mongo っぽい DB 生えたし(枯れてないけど)、プライシング気にしなければたぶんこれが最速サーバーレス構築だと思います。本番に耐えうるかは実測してみないと知らん。