Table of Contents
2022 年 10 月 25 日に Firebase Summit 2022 で Next.js と Angular Universal を用いたサーバサイドレンダリングによる動的な Web サイトにも対応することを発表しました。
これまでは、SSR を使うとき Functions の設定がいろいろ面倒でした。今回から Firebase CLI ツールが自動的に Next.js や Angular Universal を認識し、Google Cloud Functions および Firebase Hosting で稼働する設定を全て自動的に行ってくれるようになりました。
早速試してみました。
環境
- Node.js : 16.14.0
- firebase(CLI ツール) : 11.16.1
- firebase プロジェクト: Blaze 従量制 になってるプロジェクトを準備しておく
- Next:13.0.4
- React : 18.2.0
- Yarn : 1.17.0 ※手順は npm ではなく yarn を使います。
-
※ Node.js の環境構築、Firebase CLI ツール インストールの手順、Firebase プロジェクトの作成については記載しません。
1. Firebase CLI ツール アップデート
- グローバルにインストールしていた
firebase-tools
をアップデートします。ドキュメント には、11.14.2 以降と記載ありますが、不具合があるみたいで 11.16.1 のバージョンだとうまくいきました。
# アップデート
$ npm i -g firebase-tools to update
$ firebase --version
11.16.1
2. Firebase CLI で、ウェブ フレームワークのプレビューを有効にします
$ firebase experiments:enable webframeworks
3. Next.js のプロジェクト作成
- next-firebase というプロジェクト名で Next.js のプロジェクトを作成する。TypeScript を利用するとので
--ts
を付けてます。
# プロジェクトを作成するところへ移動し、Next.js のプロジェクトを作成する。next-firebase ディレクトリもできる。
$ npx create-next-app@latest --ts next-firebase
# Yesが選択されてるので Enter を押下
? Would you like to use ESLint with this project? › No / Yes
# プロジェクト直下へ移動
$ cd next-firebase
4. Firebase Hosting 初期化
- 予め Firebase のプロジェクトが作成済み状態で実行してます。プロジェクトは、Blaze 従量制 になっている必要があります。
# Hosting を初期化
$ firebase init hosting
? Please select an option: (Use arrow keys)
Use an existing project ##### すでにプロジェクトを作成しているのでここが選択された状態で Enter を押下 #####
Create a new project
Add Firebase to an existing Google Cloud Platform project
Don't set up a default project
? Select a default Firebase project for this directory: (Use arrow keys)
❯ hoge-s2s4s (hoge) ##### Blaze 従量制 になってるプロジェクトを選択 #####
hoge-d52b8 (hoge-d52b8)
? Detected an existing Next.js codebase in the current directory, should we use this? (Y/n)
##### カレントディレクトリの Next.js があるので Y を入力して押下 #####
? Set up automatic builds and deploys with GitHub? (y/N)
##### Github で自動ビルドは、いったんなにもしないので N を入力して Enter を押下 #####
5. 開発モードで動かす
- yarn dev を実行して、
http://localhost:3000/
を開くとWelcome to Next.js!
の画面が表示される。
$ yarn dev
6. SSR を実装しする
- page ディレクトリに ssr.tsx のファイルを追加する。
- サンプル実装で Yes か No で答えてくれる API を利用 https://yesno.wtf/
- SSR ソース:https://github.com/ka-yamao/next-firebase/blob/main/pages/ssr.tsx
- axios の追加もお願いします。
yarn add axios
const URL = `https://yesno.wtf/api`;
type Data = {
answer: string;
forced: boolean;
image: string;
};
type Props = {
data: Data;
};
export const getServerSideProps: GetServerSideProps = async (context) => {
const res = await axios.get<Data>(URL);
const data = res.status === 200 ? res.data : undefined;
return {
props: {
data,
} as Props,
};
};
export default function Ssr({ data }: Props) {
const { answer, forced, image } = data;
return (
// 省略
<main className={styles.main}>
<h1>SSR</h1>
<p>
YesかNoで答えてくれるAPI 1万回に1回、maybeが返ることもあるらしい。
</p>
<p>{answer}</p>
<Image src={image} width={300} height={300} alt="logo" />
</main>
// 省略
);
}
7. SSG を実装
- SSG のソース:https://github.com/ka-yamao/next-firebase/blob/main/pages/%5Bid%5D.tsx
/ssg1
、/ssg2
のダイナミックリーティングで実装、SSG なのでビルド時に生成され、リロードしても表示が変わらない。
const URL = `https://yesno.wtf/api`;
type Data = {
answer: string;
forced: boolean;
image: string;
};
type PathParams = {
id: string;
};
type PageProps = {
id: string;
data: Data;
};
export const getStaticPaths: GetStaticPaths<PathParams> = async () => {
const paths = [{ params: { id: "ssg1" } }, { params: { id: "ssg2" } }];
return {
paths,
fallback: false,
};
};
export const getStaticProps: GetStaticProps<PageProps> = async (context) => {
const { id } = context.params as PathParams;
const res = await axios.get<Data>(URL);
const props: PageProps = {
id,
data: res.data,
};
return { props };
};
// ページコンポーネントの実装
const Ids: React.FC<PageProps> = ({ id, data }: PageProps) => {
const { answer, forced, image } = data;
return (
<div className={styles.container}>
// 省略
<main className={styles.main}>
<h1>SSG {id}ページ</h1>
<p>
YesかNoで答えてくれるAPI 1万回に1回、maybeが返ることもあるらしい。
</p>
<p>{answer}</p>
<Image src={image} width={300} height={300} alt="logo" />
</main>
// 省略
);
};
export default Ids;
8. Next.js ビルド
/ssg1
と/ssg2
がちゃんと生成されているかログで確認する。
# package.json の script の build に `next build` が設定されているので yarn で実行する。
$ yarn build
ログ
yarn build
yarn run v1.17.0
$ next build
info - Linting and checking validity of types
info - Creating an optimized production build
info - Compiled successfully
info - Collecting page data
info - Generating static pages (5/5)
info - Finalizing page optimization
Route (pages) Size First Load JS
┌ ○ / 1.07 kB 77.6 kB
├ /_app 0 B 73.1 kB
├ ● /[id] (2524 ms) 959 B 77.5 kB
├ ├ /ssg2 (1265 ms)
├ └ /ssg1 (1259 ms)
├ ○ /404 181 B 73.3 kB
├ λ /api/hello 0 B 73.1 kB
└ λ /ssr 927 B 77.5 kB
+ First Load JS shared by all 73.4 kB
├ chunks/framework-8c5acb0054140387.js 45.4 kB
├ chunks/main-b482fffd82fa7e1c.js 26.7 kB
├ chunks/pages/_app-3893aca8cac41098.js 296 B
├ chunks/webpack-8fa1640cc84ba8fe.js 750 B
└ css/ab44ce7add5c3d11.css 247 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)
8. Firebase へデプロイする
- ログの途中に
npm install --save firebase-functions@latest
という警告が出てるが、Please note that there will be breaking changes when you upgrade.
アップグレードしたら動かなくなるかもとある。アップグレードしたらほんとに動かなくなります。
$ firebase deploy
ログ
Detected a Next.js codebase. This is an experimental integration, proceed with caution.
info - Linting and checking validity of types
info - Creating an optimized production build
info - Compiled successfully
info - Collecting page data
info - Generating static pages (5/5)
info - Finalizing page optimization
Route (pages) Size First Load JS
┌ ○ / 1.07 kB 77.6 kB
├ /_app 0 B 73.1 kB
├ ● /[id] (2888 ms) 959 B 77.5 kB
├ ├ /ssg2 (1451 ms)
├ └ /ssg1 (1437 ms)
├ ○ /404 181 B 73.3 kB
├ λ /api/hello 0 B 73.1 kB
└ λ /ssr 927 B 77.5 kB
+ First Load JS shared by all 73.4 kB
├ chunks/framework-8c5acb0054140387.js 45.4 kB
├ chunks/main-b482fffd82fa7e1c.js 26.7 kB
├ chunks/pages/_app-3893aca8cac41098.js 296 B
├ chunks/webpack-8fa1640cc84ba8fe.js 750 B
└ css/ab44ce7add5c3d11.css 247 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)
up to date in 2s
94 packages are looking for funding
run `npm fund` for details
=== Deploying to 'プロジェクトID'...
i deploying functions, hosting
i functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i functions: ensuring required API cloudbuild.googleapis.com is enabled...
i artifactregistry: ensuring required API artifactregistry.googleapis.com is enabled...
✔ functions: required API cloudbuild.googleapis.com is enabled
✔ artifactregistry: required API artifactregistry.googleapis.com is enabled
✔ functions: required API cloudfunctions.googleapis.com is enabled
i functions: preparing codebase firebase-frameworks-プロジェクトID for deployment
⚠ functions: package.json indicates an outdated version of firebase-functions. Please upgrade using npm install --save firebase-functions@latest in your functions directory.
⚠ functions: Please note that there will be breaking changes when you upgrade.
i functions: Loaded environment variables from .env.
i functions: preparing .firebase/プロジェクトID/functions directory for uploading...
i functions: packaged /Users/HogeHoge/workspace/next-firebase/.firebase/プロジェクトID/functions (44.16 MB) for uploading
i functions: ensuring required API run.googleapis.com is enabled...
i functions: ensuring required API eventarc.googleapis.com is enabled...
i functions: ensuring required API pubsub.googleapis.com is enabled...
i functions: ensuring required API storage.googleapis.com is enabled...
✔ functions: required API run.googleapis.com is enabled
✔ functions: required API eventarc.googleapis.com is enabled
✔ functions: required API pubsub.googleapis.com is enabled
✔ functions: required API storage.googleapis.com is enabled
i functions: generating the service identity for pubsub.googleapis.com...
i functions: generating the service identity for eventarc.googleapis.com...
✔ functions: .firebase/プロジェクトID/functions folder uploaded successfully
i hosting[プロジェクトID]: beginning deploy...
i hosting[プロジェクト]: found 23 files in .firebase/プロジェクトID/hosting
✔ hosting[プロジェクトID]: file upload complete
i functions: updating Node.js 16 function firebase-frameworks-プロジェクトID:ssrプロジェクトID(us-central1)...
✔ functions[firebase-frameworks-プロジェクトID:ssrプロジェクトID(us-central1)] Successful update operation.
Function URL (firebase-frameworks-プロジェクトID:ssrプロジェクトID4a5f3(us-central1)): https://ssrプロジェクトID-fdsafsa233-uc.a.run.app
i functions: cleaning up build files...
i hosting[プロジェクトID]: finalizing version...
✔ hosting[プロジェクトID]: version finalized
i hosting[プロジェクトID]: releasing new version...
✔ hosting[プロジェクトID]: release complete
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/プロジェクトID/overview
Hosting URL: https://プロジェクトID.web.app
表示確認
- Hosting の URL を開いて確認:
https://プロジェクト ID.web.app
- Next.js の画面が表示される。
-
SSR の確認
https://プロジェクト ID.web.app/ssr
を- API で取得した Yes or No の画像などが表示される。
- リロードのたびに変わることを確認
- SSG の確認1 https://プロジェクト ID.web.app/ssg1
- リロードのたびに変わらないこと
- リロードのたびに変わらないこと
- SSG の確認2 https://プロジェクト ID.web.app/ssg2
- リロードのたびに変わらないこと
- リロードのたびに変わらないこと
Firebase を確認
- https://console.firebase.google.com/?hl=ja
- Hosting にプロジェクトがデプロイされていること
- Functions に ssr の関数がデプロイされていること
まとめ
Firebase のデプロイが楽ちんで便利になりました。
Vercel へデプロイ、自前のサーバーにデプロイするもの面倒で Firebase のほうが個人的に使い勝手がいいので 10 月 25 日の発表はとれもうれしかった。
Analytics もあるし、最近 Firebase を利用しないケースがないので Hosting を使っての Web アプリ開発もどんどんやってみたい。
SEO を気にしつつ Next.js で Web アプリを作っていきたい。
デプロイのときハマった
- Firebase CLI ツールが古かった影響でデプロイのときこんなログがでました。11.16.1 のバージョンだとうまくいきました。
Unable to find a valid endpoint for function `ssrプロジェクトID`, but still including it in the config
参考資料
- Next.js を統合する : https://firebase.google.com/docs/hosting/nextjs
サンプルソース
- Github : https://github.com/ka-yamao/next-firebase/