Cloud Functionsは、イベントドリブンサーバーレスプラットフォームです。コードをuploadすると、HTTPリクエスト経由で呼び出すことができます。
Next.jsで作ったクライアントサイドのアプリをホスティングします。
これは、「Cloud Functions を使用した動的コンテンツの配信とマイクロサービスのホスティング」というやり方らしいです。
プロジェクトの作成
を参考に作成します。
node -v: v15.9.0
だった。versionについては調べずにざっくり作業していきます。
npx create-next-app --example with-firebase-hosting with-firebase-hosting-app
.firebaserc
にプロジェクトIDを書き込む。
{
"projects": {
"default": "hoge-XXXXXX"
}
}
firebase use default
で、defaultプロジェクトを現在の「アクティブ プロジェクト」として使用します。
Deploy
npm run deploy
> deploy
> firebase deploy --only functions,hosting
=== Deploying to 'hoge-XXXXXX'...
i deploying functions, hosting
Running command: npm --prefix "$PROJECT_DIR" install
up to date, audited 1020 packages in 2s
72 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Running command: npm --prefix "$PROJECT_DIR" run build
> build
> next build src/
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
info - Checking validity of types
warn - No ESLint configuration detected. Run next lint to begin setup
info - Creating an optimized production build
info - Compiled successfully
info - Collecting page data
info - Generating static pages (4/4)
info - Finalizing page optimization
Page Size First Load JS
┌ ○ / 1.76 kB 68.7 kB
├ ○ /404 194 B 67.1 kB
└ ○ /about 1.76 kB 68.7 kB
+ First Load JS shared by all 66.9 kB
├ chunks/framework.65bc65.js 42 kB
├ chunks/main.870523.js 23.1 kB
├ chunks/pages/_app.9bccc5.js 977 B
└ chunks/webpack.61f1b6.js 778 B
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
○ (Static) automatically rendered as static HTML (uses no initial props)
● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
(ISR) incremental static regeneration (uses revalidate in getStaticProps)
✔ functions: Finished running predeploy script.
i functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i functions: ensuring required API cloudbuild.googleapis.com is enabled...
✔ functions: required API cloudfunctions.googleapis.com is enabled
⚠ functions: missing required API cloudbuild.googleapis.com. Enabling now...
Error: Your project hoge-XXXXX must be on the Blaze (pay-as-you-go) plan to complete this command. Required API cloudbuild.googleapis.com can't be enabled until the upgrade is complete. To upgrade, visit the following URL:
Your project hoge-XXXXX must be on the Blaze (pay-as-you-go) plan to complete this command. Required API cloudbuild.googleapis.com can't be enabled until the upgrade is complete. To upgrade, visit the following URL:
「Blazeプランじゃないとコマンドは完了できないよ」という文言が丁寧に表示されます。従量課金プランじゃないとダメなようです。
Blazeプランに変更した
npm run deploy
を再度試みる。
> deploy
> firebase deploy --only functions,hosting
=== Deploying to 'hoge-XXXXXX'...
i deploying functions, hosting
Running command: npm --prefix "$PROJECT_DIR" install
up to date, audited 1020 packages in 2s
72 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Running command: npm --prefix "$PROJECT_DIR" run build
> build
> next build src/
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
info - Checking validity of types
warn - No ESLint configuration detected. Run next lint to begin setup
info - Creating an optimized production build
info - Compiled successfully
info - Collecting page data
info - Generating static pages (4/4)
info - Finalizing page optimization
Page Size First Load JS
┌ ○ / 1.76 kB 68.7 kB
├ ○ /404 194 B 67.1 kB
└ ○ /about 1.76 kB 68.7 kB
+ First Load JS shared by all 66.9 kB
├ chunks/framework.65bc65.js 42 kB
├ chunks/main.870523.js 23.1 kB
├ chunks/pages/_app.9bccc5.js 977 B
└ chunks/webpack.61f1b6.js 778 B
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
○ (Static) automatically rendered as static HTML (uses no initial props)
● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
(ISR) incremental static regeneration (uses revalidate in getStaticProps)
✔ functions: Finished running predeploy script.
i functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i functions: ensuring required API cloudbuild.googleapis.com is enabled...
⚠ functions: missing required API cloudbuild.googleapis.com. Enabling now...
✔ functions: required API cloudfunctions.googleapis.com is enabled
✔ functions: required API cloudbuild.googleapis.com is enabled
i functions: preparing . directory for uploading...
i functions: packaged . (9.91 MB) for uploading
✔ functions: . folder uploaded successfully
i hosting[hoge-XXXXXX]: beginning deploy...
i hosting[hoge-XXXXXX]: found 0 files in public
✔ hosting[hoge-XXXXXX]: file upload complete
i functions: creating Node.js 14 function nextjsFunc(us-central1)...
✔ functions[nextjsFunc(us-central1)]: Successful create operation.
i functions: cleaning up build files...
Function URL (nextjsFunc(us-central1)): https://us-central1-hoge-XXXXXX.cloudfunctions.net/nextjsFunc
i hosting[hoge-XXXXXX]: finalizing version...
✔ hosting[hoge-XXXXXX]: version finalized
i hosting[hoge-XXXXXX]: releasing new version...
✔ hosting[hoge-XXXXXX]: release complete
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/hoge-XXXXXX/overview
Hosting URL: https://hoge-XXXXXX.web.app
Hosting URLにアクセスすると確かに表示されました。
SSGとSSR
Next.jsはSSRとSSGに対応しているのでその辺の挙動を確認してみます。
SSGの確認
src/index.jsx
を以下のように変更します。
import App from "../components/App";
export default function Home(props) {
return (
<App>
<div>
<div>{props.isSSR ? <h2>SSR Working</h2> : <h2>SSR Not Works</h2>}</div>
<div>
{props.isStatic ? <h2>Static generated</h2> : <h2>is Not Static</h2>}
</div>
</div>
</App>
);
}
export async function getStaticProps() {
return { props: { isStatic: true } };
}
ローカルで動かした場合
npm run dev
Firebaseにdeployしてみます。
npm run deploy
URLをうすしてないので無意味な画像ですが、確かに、Static Generatedと表示されています。
SSRの確認
import App from "../components/App";
export default function Home(props) {
return (
<App>
<div>
<div>{props.isSSR ? <h2>SSR Working</h2> : <h2>SSR Not Works</h2>}</div>
<div>
{props.isStatic ? <h2>Static generated</h2> : <h2>is Not Static</h2>}
</div>
</div>
</App>
);
}
export async function getServerSideProps() {
return { props: { isSSR: true } };
}
ローカルで動かす
npm run dev
Deployしてみましょう。
まあ、ローカルと一緒ですね。
getServerSideProps
とgetStaticProps
は同時に使えません。ビルドエラーが出ます。
理解する。
ルートディレクトリにfirebaseFunctions.js
なるファイルがあります。
const { join } = require('path')
const { https } = require('firebase-functions')
const { default: next } = require('next')
const nextjsDistDir = join('src', require('./src/next.config.js').distDir)
const nextjsServer = next({
dev: false,
conf: {
distDir: nextjsDistDir,
},
})
const nextjsHandle = nextjsServer.getRequestHandler()
exports.nextjsFunc = https.onRequest((req, res) => {
return nextjsServer.prepare().then(() => nextjsHandle(req, res))
})
nextjsFunc
という関数が定義されています。これは、firebase.json
でhosting.rewrites
の中のfunctions
で宣言されています。
{
"hosting": {
"public": "public",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"function": "nextjsFunc"
}
]
},
"functions": {
"source": ".",
"predeploy": [
"npm --prefix \"$PROJECT_DIR\" install",
"npm --prefix \"$PROJECT_DIR\" run build"
],
"runtime": "nodejs14"
}
}
predeploy
でinstallやbuildをしています。
Cloud Functionsでのリクエストの処理がどういう挙動になっているかというと、
- まずFirebase Hostingにリクエストが送られる
- リクエストの送り先が、
nexjsFunc
というCloud Functionsの関数へと書き換えられる -
firebaseFunctions.js
の中のnexjsFunc
関数がリクエストを処理し、Next.jsのサーバーとしてレンダリングし、レスポンスを返す
となっているようです。
firebaseFunctions.js
の正体は、Cloud Function For Firebaseにデプロイするjsファイルでした。package.json
のなかでmain
keyのところで設定されています。
{
"private": true,
"main": "firebaseFunctions.js",
}
他にもいろいろ、Firebaseの設定ファイルがあります。Next.jsのレポジトリにドキュメントがあるので見てみましょう。
この辺を参考にしました。
参考
- https://qiita.com/1amageek/items/0d01dca2148df345224a
- https://qiita.com/takaken/items/8ed671486de804696079
- https://qiita.com/centerfield77/items/349ae9094872168ae86f
- https://dev.to/rowaxl/what-i-struggled-with-next-js-using-firebase-hosting-and-enable-ssr-4e67
- https://zenn.dev/ucwork/articles/67663455297629