Serverless Next.jsをFirebaseに置いたときのメモ
Next.js の v8 からサーバーレス向けのビルドができるようになった。
Next.js のビルド自体はインストラクションとおりやればできるのだが、それをデプロイするときにちょっとハマったのでそれを簡単にメモしておく。
デプロイ先は使い慣れた Firebase を使った。
自分のサイトもこの方法で運用している。
Next.js でプロジェクトを作成
公式サイトを見ながらやればきっとできる。
Firebase Functions のディレクトリにビルド
Firebase Functions のディレクトリ内に next のビルドが向くように設定をする。
next.config.js
const withTypescript = require("@zeit/next-typescript");
module.exports = {
...withTypescript(),
distDir: "functions/build",
target: "serverless",
};
こうすることで Firebase Functions からビルドした js が参照できるようになる。
Firebase Functions で返す
ビルドした JavaScript とエンドポイントをマッピング
ビルドした js を使って実際 Functions からビルドした js が実行されるようにマッピングする。
functions/index.js
const functions = require("Firebase-functions");
const indexPage = require("./build/serverless/pages/index");
// const messagePage = require("./build/serverless/pages/message");
const architecturePage = require("./build/serverless/pages/architecture");
exports.index = functions.https.onRequest((request, response) => {
indexPage.render(request, response);
});
next 自体は TypeScript で書いているが、Firebase Functions はビルドされた next の JavaScript を見るだけなので、Firebase Functions は TypeScript ではなく JavaScript でやるのがお勧め。
ただrequire
してrender
するだけ。
Firebase のルーティング設定
エンドポイントの設定
Firebase はデフォルトで hosting にルーティングされるので、設定した Functions に redirect されるように記述する。
firebase.json
{
"hosting": {
"public": "public",
"cleanUrls": true,
"rewrites": [
{
"source": "/static/*",
"destination": "/*"
},
{
"source": "/",
"function": "index"
},
]
},
"functions": {
"predeploy": ["yarn --prefix \"$RESOURCE_DIR\" build", "yarn export"],
"source": "functions"
}
}
画像などのアセットへのパス
画像は SSR では含まれず、クライアント側から再度リクエストが飛んでくる。それを返すパスの設定もする必要がある。
実際の URL はstatic/assets/images/avatar.jpg
という感じになる。つまり、next.js では画像など静的ファイルは/static/
次のファイルに入れる仕様なので、/static/*
は hosting でいい感じに処理されるように/*
にパスを書き換える。
Firebase hosting では/public/
が配信される設定なので、ビルド時に
cp -r static public/
するといい感じに配信される。
バンドルされた JS のパス
SSR なので処理レンダーはサーバー側でなされるが、それ以降はクライアント側で JS を読み込んで実行される。
その JS をフェッチするリクエストは別途捌く必要がある。
URL を見てみると_next/static/hogehoge/pages/index.js
のようになっているので、public/_next/static/
次にビルドされたファイルが入っていれば配信されるはず。
なので、
cp -r functions/build/static/* public/_next/static
もビルド時にする必要がある。
つまりどうするのか
ただビルドしたやつを Functions にマッピングするだけではなく、静的ファイルの配信もビルドしたものをいい感じに配置していかなければ動かないので意外とめんどくさい。
他にも賢い方法があるかもしれないが、とりあえず以下を実行すればすべてビルドしてデプロイまでされるようになっている。
yarn deploy
package.json はこんな感じになっている。
package.json
{
"scripts": {
"dev": "next",
"build": "next build",
"export": "rm -rf public && mkdir public public/_next public/_next/static && cp -r static public/ && cp -r functions/build/static/* public/_next/static",
"deploy": "yarn build && Firebase deploy"
}
}
yarn export
に前述したファイルのcp
処理を書いている。ちょっとディレクトリ消したり作ったり移動したりしていてあまりきれいじゃないのでどうにかしたいとは思っている。
そして、Firebase のデプロイにフックして、事前に Functions のビルドとファイル移動が走るようになっている。
firebase.json
{
"functions": {
"predeploy": ["yarn --prefix \"$RESOURCE_DIR\" build", "yarn export"],
"source": "functions"
}
}
こうすることで汚いけれど、いい感じに Functions で捌きつつ静的ファイル配信ができるようになっているはず。