1. 事前準備
在本程式碼實驗室中,您將瞭解如何將 Firebase 整合至名為 Friendly Eats 的 Next.js 網頁應用程式,這是一個餐廳評論網站。

完成的網頁應用程式提供實用功能,展示 Firebase 如何協助您建構 Next.js 應用程式。這些功能包括:
- 自動建構及部署:本程式碼實驗室會使用 Firebase App Hosting,在您每次推送至設定的分支版本時,自動建構及部署 Next.js 程式碼。
- 登入和登出:完成的網頁應用程式可讓您使用 Google 帳戶登入及登出。使用者登入和持續性完全透過 Firebase 驗證管理。
- 圖片:登入的使用者可以透過完成的網頁應用程式上傳餐廳圖片。圖片素材資源會儲存在 Cloud Storage for Firebase。Firebase JavaScript SDK 會為上傳的圖片提供公開網址。接著,這個公開網址會儲存在 Cloud Firestore 的相關餐廳文件中。
- 評論:已登入的使用者可透過完成的網頁應用程式,發布餐廳評論,包括星級評等和文字訊息。評論資訊會儲存在 Cloud Firestore。
- 篩選器:完成的網頁應用程式可讓已登入的使用者依類別、位置和價格篩選餐廳清單。您也可以自訂排序方法。系統會從 Cloud Firestore 存取資料,並根據使用的篩選條件套用 Firestore 查詢。
必要條件
- GitHub 帳戶
- 具備 Next.js 和 JavaScript 知識
課程內容
- 如何搭配使用 Firebase 與 Next.js App Router 和伺服器端轉譯。
- 如何將圖片保存在 Cloud Storage for Firebase。
- 如何在 Cloud Firestore 資料庫中讀取及寫入資料。
- 如何搭配使用使用 Google 帳戶登入功能和 Firebase JavaScript SDK。
事前準備
- Git
- Node.js 的最新穩定版本
- 您選擇的瀏覽器,例如 Google Chrome
- 具備程式碼編輯器和終端機的開發環境
- 用於建立及管理 Firebase 專案的 Google 帳戶
- 將 Firebase 專案升級至 Blaze 定價方案
2. 設定開發環境和 GitHub 存放區
本程式碼實驗室提供應用程式的範例程式碼集,並會使用 Firebase CLI。
建立 GitHub 存放區
程式碼實驗室原始碼位於 https://github.com/firebase/friendlyeats-web。這個存放區包含多個平台的範例專案。不過,本程式碼實驗室只會使用 nextjs-start 目錄。請注意下列目錄:
* `nextjs-start`: contains the starter code upon which you build.
* `nextjs-end`: contains the solution code for the finished web app.
將 nextjs-start 資料夾複製到自己的存放區:
- 使用終端機在電腦上建立新資料夾,並切換至新目錄:
mkdir codelab-friendlyeats-web cd codelab-friendlyeats-web - 使用 giget npm 套件,只擷取
nextjs-start資料夾:npx giget@latest "gh:firebase/friendlyeats-web/nextjs-start#master" . --install - 使用 git 在本機追蹤變更:
git init git add . git commit -m "Codelab starting point" git branch -M main - 建立新的 GitHub 存放區:https://github.com/new。命名方式不限。
- 視您驗證 GitHub 的方式 (HTTPS 或 SSH) 而定,複製 GitHub 為您建立的新網址:
https://github.com/<USER_NAME>/<REPOSITORY_NAME>.git或git@github.com:<USER_NAME>/<REPOSITORY_NAME>.git
- 執行下列指令,將本機變更推送至新的 GitHub 存放區。將
<REPOSITORY_URL>預留位置替換為實際的存放區網址。git remote add origin <REPOSITORY_URL> git push -u origin main - 現在,您應該會在 GitHub 存放區中看到範例程式碼。
安裝或更新 Firebase CLI
執行下列指令,確認您已安裝 Firebase CLI,且版本為 14.1.0 以上:
firebase --version
如果版本較低或未安裝 Firebase CLI,請執行安裝指令:
npm install -g firebase-tools@latest
如果因權限錯誤而無法安裝 Firebase CLI,請參閱 npm 說明文件,或使用其他安裝選項。
登入 Firebase
- 執行下列指令,登入 Firebase CLI:
firebase login
- 根據您是否要讓 Firebase 收集資料,輸入
Y或N。 - 在瀏覽器中選取 Google 帳戶,然後按一下「允許」。
3. 設定 Firebase 專案
在本節中,您將設定 Firebase 專案,並連結 Firebase 網頁應用程式。您也會設定範例網頁應用程式使用的 Firebase 服務。
建立 Firebase 專案
- 使用上一步驟所用的 Google 帳戶登入 Firebase 控制台。
- 按一下按鈕建立新專案,然後輸入專案名稱 (例如
FriendlyEats Codelab)。
- 按一下「繼續」。
- 如果出現提示訊息,請詳閱並接受 Firebase 條款,然後點選「繼續」。
- (選用) 在 Firebase 控制台中啟用 AI 輔助功能 (稱為「Gemini in Firebase」)。
- 在本程式碼實驗室中,您不需要 Google Analytics,因此請關閉 Google Analytics 選項。
- 按一下「Create project」,等待專案佈建完成,然後點選「Continue」。
升級 Firebase 定價方案
如要使用 Firebase App Hosting 和 Cloud Storage for Firebase,Firebase 專案必須採用即付即用 (Blaze) 定價方案,也就是連結至 Cloud Billing 帳戶。
- Cloud Billing 帳戶必須設定付款方式,例如信用卡。
- 如果您剛開始使用 Firebase 和 Google Cloud,請確認是否符合 $300 美元抵免額和免費試用 Cloud Billing 帳戶的資格。
- 如果您是參加活動而進行這個程式碼實驗室,請詢問主辦單位是否有可用的 Cloud 抵免額。
如要將專案升級至 Blaze 方案,請按照下列步驟操作:
- 在 Firebase 控制台中,選取「升級方案」。
- 選取 Blaze 方案。按照畫面上的指示,將 Cloud Billing 帳戶連結至專案。
如果需要建立 Cloud Billing 帳戶才能完成升級,你可能要返回 Firebase 控制台的升級流程,才能完成升級。
在 Firebase 專案中新增網頁應用程式
- 在 Firebase 專案中前往「專案總覽」,然後依序點選「新增應用程式」和「網頁」。
- 在「App nickname」(應用程式暱稱) 文字方塊中,輸入容易記住的應用程式暱稱,例如
My Next.js app。 - 請勿勾選「Also set up Firebase Hosting for this app」核取方塊。
- 依序點選「註冊應用程式」>「前往控制台」。
在 Firebase 控制台中設定 Firebase 服務
設定驗證
- 在 Firebase 控制台的左側面板中,展開「Build」,然後選取「Authentication」。
- 按一下「開始使用」。
- 在「Sign-in providers」欄中,依序點選「Google」>「Enable」。
- 在「專案的公開名稱」文字方塊中,輸入容易記住的名稱,例如
My Next.js app。 - 在「專案支援電子郵件」下拉式選單中,選取您的電子郵件地址。
- 按一下「儲存」。
設定 Cloud Firestore
- 在 Firebase 控制台的左側面板中,展開「Build」,然後選取「Firestore Database」。
- 按一下 [Create database] (建立資料庫)。
- 選擇「Standard 版」,然後點選「下一步」。
- 請勿變更「資料庫 ID」,保留
(default)即可。 - 選取資料庫的位置,然後點選「下一步」。
如果是實際應用程式,建議選擇靠近使用者的位置。 - 按一下「Start in test mode」(以測試模式啟動)。請詳閱安全性規則免責事項。
在本程式碼實驗室的後續步驟中,您將新增安全性規則來保護資料。請勿在未新增資料庫安全性規則的情況下,公開發布或公開應用程式。 - 點選「建立」。
設定 Cloud Storage for Firebase
- 在 Firebase 控制台的左側面板中,展開「Build」,然後選取「Storage」。
- 按一下「開始使用」。
- 選取預設 Storage bucket 的位置。
US-WEST1、US-CENTRAL1和US-EAST1中的 bucket 可享有 Google Cloud Storage 的「一律免費」級別。所有其他位置的值區則遵循 Google Cloud Storage 定價和用量。 - 按一下「Start in test mode」(以測試模式啟動)。請詳閱安全性規則免責事項。
在本程式碼實驗室的後續步驟中,您將新增安全性規則來保護資料。請勿在未新增 Storage bucket 安全性規則的情況下,公開發布或公開應用程式。 - 點選「建立」。
部署安全性規則
程式碼已包含 Firestore 和 Cloud Storage for Firebase 的安全性規則。部署安全性規則後,資料庫和值區中的資料就能更妥善地受到保護,避免遭到濫用。
- 在終端機中,將 CLI 設為使用先前建立的 Firebase 專案:
系統提示輸入別名時,請輸入firebase use --add
friendlyeats-codelab。 - 如要部署這些安全性規則 (以及稍後需要的索引),請在終端機中執行下列指令:
firebase deploy --only firestore,storage
- 如果系統詢問:
"Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?",請按下Enter選取「Yes」。
4. 查看範例程式碼集
在本節中,您將查看應用程式範例程式碼集中的幾個部分,並在本程式碼實驗室中為這些部分新增功能。
資料夾和檔案結構
下表概略說明應用程式的資料夾和檔案結構:
資料夾和檔案 | 說明 |
| 篩選器、標頭、餐廳詳細資料和評論的 React 元件 |
| 不一定繫結至 React 或 Next.js 的公用函式 |
| Firebase 專屬程式碼和 Firebase 設定 |
| 網頁應用程式中的靜態資產,例如圖示 |
| 使用 Next.js App Router 轉送 |
| 使用 npm 處理專案依附元件 |
| Next.js 專屬設定 (啟用伺服器動作) |
| JavaScript 語言服務設定 |
伺服器和用戶端元件
這個應用程式是使用 App Router 的 Next.js 網頁應用程式。整個應用程式都會使用伺服器轉譯。舉例來說,src/app/page.js 檔案是負責主頁面的伺服器元件。src/components/RestaurantListings.jsx 檔案是開頭有 "use client" 指令的用戶端元件。
匯入陳述式
您可能會看到類似以下的匯入陳述式:
import RatingPicker from "@/src/components/RatingPicker.jsx";
應用程式使用 @ 符號,避免使用冗長的相對匯入路徑,並透過路徑別名實現。
Firebase 專屬 API
所有 Firebase API 程式碼都包裝在 src/lib/firebase 目錄中。接著,個別 React 元件會從 src/lib/firebase 目錄匯入包裝函式,而不是直接匯入 Firebase 函式。
模擬資料
src/lib/randomData.js 檔案包含模擬餐廳和評論資料。該檔案中的資料會彙整至 src/lib/fakeRestaurants.js 檔案的程式碼中。
5. 建立 App Hosting 後端
在本節中,您將設定 App Hosting 後端,監控 Git 存放區中的分支版本。
完成本節後,您將擁有一個連結至 GitHub 存放區的 App Hosting 後端,每當您將新修訂版本推送至 main 分支版本時,該後端就會自動重新建構並推出新版應用程式。
建立後端
- 前往 Firebase 控制台的「App Hosting」頁面。
- 點選「Get started」(開始使用),啟動後端建立流程。
- 選擇您要搜尋的區域。如果是實際應用程式,請選擇最靠近使用者的區域。
- 按照「Import a GitHub repository」(匯入 GitHub 存放區) 步驟中的提示,設定 GitHub 驗證。
- 在「存放區」中,選取「授予 GitHub 新存放區的存取權」,然後按照提示啟用您先前建立的 GitHub 存放區存取權。
- 按一下「Refresh list」(重新整理清單) 重新整理清單,然後選取存放區並點選「Next」(下一步)。
- 設定部署作業:
- 將使用中的分支版本設為
main。 - 將根目錄保留為
/。 - 啟用自動推出功能。
- 將使用中的分支版本設為
- 將後端命名為
friendlyeats-codelab,然後點選「Next」(下一步)。 - 在「連結 Firebase 網頁應用程式」部分,選擇「選取現有的 Firebase 網頁應用程式」,然後從清單中選取您新增的應用程式
- 按一下「完成並部署」。系統會將您重新導向至新頁面,顯示新 App Hosting 後端的狀態!
- 按一下「查看」,即可查看 App Hosting 部署作業的詳細資訊,包括推出狀態、記錄和用量詳細資料。
- 推出作業完成後,點選「網域」下方的網站網址,由於 DNS 傳播,這項作業可能需要幾分鐘才會開始。
- 糟糕!載入頁面時,您會看到「Application error: a 伺服器端 exception has occurred (see the 伺服器記錄 for more information)」錯誤訊息。
- 在 Firebase 控制台中,查看 App Hosting 後端的「Logs」(記錄) 分頁。您會看到「Error: not implemented」記錄。我們會在下一個步驟新增驗證機制,修正這個問題。
您已部署初始網頁應用程式!每當您將新的修訂版本推送至 GitHub 存放區的 main 分支版本時,Firebase 控制台就會開始新的建構和推出作業,並在推出作業完成後自動更新網站。
6. 為網頁應用程式新增驗證機制
在本節中,您將為網頁應用程式新增驗證機制,以便登入。
新增授權網域
Firebase 驗證只會接受來自允許網域的登入要求。接著,將 App Hosting 後端的網域,新增至專案的核准網域清單。
- 開啟「App Hosting」頁面,然後點選已部署網站下方的「查看」,前往「總覽」頁面。複製 App Hosting 後端的網域名稱。
- 前往「驗證設定」分頁,選擇要新增授權網域的專案。接著,找到並點選「授權網域」部分。
- 點按「新增網域」按鈕。
- 輸入 App Hosting 後端的網域。
- 按一下 [新增]。
實作登入和登出函式
在 src/lib/firebase/auth.js 檔案中,將 onAuthStateChanged、onIdTokenChanged、signInWithGoogle 和 signOut 函式替換為下列程式碼:
export function onAuthStateChanged(cb) {
return _onAuthStateChanged(auth, cb);
}
export function onIdTokenChanged(cb) {
return _onIdTokenChanged(auth, cb);
}
export async function signInWithGoogle() {
const provider = new GoogleAuthProvider();
try {
await signInWithPopup(auth, provider);
} catch (error) {
console.error("Error signing in with Google", error);
}
}
export async function signOut() {
try {
return auth.signOut();
} catch (error) {
console.error("Error signing out with Google", error);
}
}
這段程式碼會使用下列 Firebase API:
Firebase API | 說明 |
新增觀察器,監控使用者登入狀態的變化。 | |
新增觀察器,監控使用者 ID 權杖的變更。 | |
建立 Google 驗證提供者執行個體。 | |
啟動對話方塊式驗證流程。 | |
將使用者登出。 |
在 src/components/Header.jsx 檔案中,程式碼已叫用 signInWithGoogle 和 signOut 函式。
將驗證狀態傳送至伺服器
為了將驗證狀態傳遞至伺服器,我們會使用 Cookie。每當用戶端的驗證狀態變更時,我們就會更新 __session Cookie。
在 src/components/Header.jsx 中,請使用以下程式碼取代 useUserSession 函式:
function useUserSession(initialUser) {
useEffect(() => {
return onIdTokenChanged(async (user) => {
if (user) {
const idToken = await user.getIdToken();
await setCookie("__session", idToken);
} else {
await deleteCookie("__session");
}
if (initialUser?.uid === user?.uid) {
return;
}
window.location.reload();
});
}, [initialUser]);
return initialUser;
}
讀取伺服器上的驗證狀態
我們將使用 FirebaseServerApp,在伺服器上鏡像用戶端的驗證狀態。
開啟 src/lib/firebase/serverApp.js,並替換 getAuthenticatedAppForUser 函式:
export async function getAuthenticatedAppForUser() {
const authIdToken = (await cookies()).get("__session")?.value;
// Firebase Server App is a new feature in the JS SDK that allows you to
// instantiate the SDK with credentials retrieved from the client & has
// other affordances for use in server environments.
const firebaseServerApp = initializeServerApp(
// https://github.com/firebase/firebase-js-sdk/issues/8863#issuecomment-2751401913
initializeApp(),
{
authIdToken,
}
);
const auth = getAuth(firebaseServerApp);
await auth.authStateReady();
return { firebaseServerApp, currentUser: auth.currentUser };
}
確認變更
src/app/layout.js 檔案中的根層級版面配置會轉譯標頭,並將使用者 (如有) 做為屬性傳入。
<Header initialUser={currentUser?.toJSON()} />
這表示 <Header> 元件會在伺服器執行階段轉譯使用者資料 (如有)。如果網頁在初始載入後,於生命週期內有任何驗證更新,onAuthStateChanged 處理常式會處理這些更新。
現在可以推出新版本,並驗證建構內容。
- 建立修訂版本,並附上修訂訊息「Add authentication」,然後推送至 GitHub 存放區。
git add . git commit -m "Add authentication" git push - 開啟 App Hosting 頁面,在新推出作業完成後,點選網站網址開啟網站。
- 測試驗證:
- 使用 Google 帳戶登入,並確認登入後標頭中顯示您的顯示名稱。
- 先登出再登入一次。您可以對不同使用者重複這個步驟。
- 選用:在網頁應用程式上按一下滑鼠右鍵,選取「檢視網頁原始碼」,然後搜尋顯示名稱。出現在伺服器傳回的原始 HTML 原始碼中。
7. 查看餐廳資訊
網頁應用程式包含餐廳和評論的模擬資料。
新增一或多間餐廳
如要將模擬餐廳資料插入本機 Cloud Firestore 資料庫,請按照下列步驟操作:
- 登入網頁應用程式 (如果尚未登入)。然後依序選取「
」>「新增餐廳範例」。請注意,Friendly Eats 網頁應用程式中不會顯示任何餐廳,因為我們尚未設定資料擷取程式碼。我們會在下一個步驟修正這個問題。 - 在 Firebase 控制台的「Firestore Database」頁面,選取「restaurants」。您會看到餐廳集合中的頂層文件,每份文件代表一間餐廳。
- 點選幾份文件,探索餐廳文件的屬性。
顯示餐廳清單
Cloud Firestore 資料庫現在有餐廳資料,可供 Next.js 網頁應用程式顯示。
如要定義資料擷取程式碼,請按照下列步驟操作:
- 在
src/app/page.js檔案中,找到<Home />伺服器元件,並查看對getRestaurants函式的呼叫,該函式會在伺服器執行階段擷取餐廳清單。您將在後續步驟中實作getRestaurants函式。 - 在
src/lib/firebase/firestore.js檔案中,將applyQueryFilters和getRestaurants函式替換為下列程式碼:
function applyQueryFilters(q, { category, city, price, sort }) {
if (category) {
q = query(q, where("category", "==", category));
}
if (city) {
q = query(q, where("city", "==", city));
}
if (price) {
q = query(q, where("price", "==", price.length));
}
if (sort === "Rating" || !sort) {
q = query(q, orderBy("avgRating", "desc"));
} else if (sort === "Review") {
q = query(q, orderBy("numRatings", "desc"));
}
return q;
}
export async function getRestaurants(db = db, filters = {}) {
let q = query(collection(db, "restaurants"));
q = applyQueryFilters(q, filters);
const results = await getDocs(q);
return results.docs.map((doc) => {
return {
id: doc.id,
...doc.data(),
// Only plain objects can be passed to Client Components from Server Components
timestamp: doc.data().timestamp.toDate(),
};
});
}
- 建立修訂版本,並附上修訂訊息「Read the list of restaurants from Firestore」,然後推送至 GitHub 存放區。
git add . git commit -m "Read the list of restaurants from Firestore" git push - 在 Firebase 控制台中開啟 App Hosting 頁面,等待新的推出作業完成。
- 在網頁應用程式中重新整理頁面。餐廳圖片會以圖塊形式顯示在頁面上。
確認餐廳資訊會在伺服器執行階段載入
使用 Next.js 架構時,可能無法明顯看出資料是在伺服器執行階段或用戶端執行階段載入。
如要確認餐廳資訊是否會在伺服器執行階段載入,請按照下列步驟操作:
- 在網頁應用程式中開啟開發人員工具,並停用 JavaScript。

- 重新整理網頁應用程式,餐廳清單仍會載入。伺服器回應會傳回餐廳資訊。啟用 JavaScript 後,系統會透過用戶端 JavaScript 程式碼補充餐廳資訊。
- 在開發人員工具中,重新啟用 JavaScript。
使用 Cloud Firestore 快照監聽器監聽餐廳更新
在上一節中,您已瞭解如何從 src/app/page.js 檔案載入初始餐廳組合。src/app/page.js 檔案是伺服器元件,會在伺服器上算繪,包括 Firebase 資料擷取程式碼。
src/components/RestaurantListings.jsx 檔案是用戶端元件,可設定為對伺服器轉譯的標記進行凍結。
如要設定 src/components/RestaurantListings.jsx 檔案,以補充伺服器轉譯的標記,請按照下列步驟操作:
- 在
src/components/RestaurantListings.jsx檔案中,查看下列已為您編寫的程式碼:
useEffect(() => {
return getRestaurantsSnapshot((data) => {
setRestaurants(data);
}, filters);
}, [filters]);
這段程式碼會叫用 getRestaurantsSnapshot() 函式,類似於您在先前步驟中實作的 getRestaurants() 函式。不過,這個快照函式提供回呼機制,因此每次餐廳集合有變更時,都會叫用回呼。
- 在
src/lib/firebase/firestore.js檔案中,將getRestaurantsSnapshot()函式替換為下列程式碼:
export function getRestaurantsSnapshot(cb, filters = {}) {
if (typeof cb !== "function") {
console.log("Error: The callback parameter is not a function");
return;
}
let q = query(collection(db, "restaurants"));
q = applyQueryFilters(q, filters);
return onSnapshot(q, (querySnapshot) => {
const results = querySnapshot.docs.map((doc) => {
return {
id: doc.id,
...doc.data(),
// Only plain objects can be passed to Client Components from Server Components
timestamp: doc.data().timestamp.toDate(),
};
});
cb(results);
});
}
- 在
src/lib/firebase/firestore.js檔案中,將getRestaurantSnapshotById()函式替換為下列程式碼:
export function getRestaurantSnapshotById(restaurantId, cb) {
if (!restaurantId) {
console.log("Error: Invalid ID received: ", restaurantId);
return;
}
if (typeof cb !== "function") {
console.log("Error: The callback parameter is not a function");
return;
}
const docRef = doc(db, "restaurants", restaurantId);
return onSnapshot(docRef, (docSnap) => {
cb({
...docSnap.data(),
timestamp: docSnap.data().timestamp.toDate(),
});
});
}
透過「Firestore 資料庫」頁面所做的變更,現在會即時反映在網頁應用程式中。
- 建立修訂版本,並附上修訂訊息「Listen for realtime restaurant updates」,然後推送至 GitHub 存放區。
git add . git commit -m "Listen for realtime restaurant updates" git push - 在 Firebase 控制台中開啟 App Hosting 頁面,等待新的推出作業完成。
- 在網頁應用程式中,依序選取「
」>「Add sample restaurants」。如果快照函式實作正確,餐廳就會即時顯示,不需重新整理頁面。
8. 儲存使用者透過網頁應用程式提交的評論
- 在
src/lib/firebase/firestore.js檔案中,將updateWithRating()函式替換為下列程式碼:
const updateWithRating = async (
transaction,
docRef,
newRatingDocument,
review
) => {
const restaurant = await transaction.get(docRef);
const data = restaurant.data();
const newNumRatings = data?.numRatings ? data.numRatings + 1 : 1;
const newSumRating = (data?.sumRating || 0) + Number(review.rating);
const newAverage = newSumRating / newNumRatings;
transaction.update(docRef, {
numRatings: newNumRatings,
sumRating: newSumRating,
avgRating: newAverage,
});
transaction.set(newRatingDocument, {
...review,
timestamp: Timestamp.fromDate(new Date()),
});
};
這段程式碼會插入新的 Firestore 文件,代表新的評論。程式碼也會更新代表餐廳的現有 Firestore 文件,並提供更新後的評分數量和平均計算評分。
- 將
addReviewToRestaurant()函式替換為下列程式碼:
export async function addReviewToRestaurant(db, restaurantId, review) {
if (!restaurantId) {
throw new Error("No restaurant ID has been provided.");
}
if (!review) {
throw new Error("A valid review has not been provided.");
}
try {
const docRef = doc(collection(db, "restaurants"), restaurantId);
const newRatingDocument = doc(
collection(db, `restaurants/${restaurantId}/ratings`),
);
// corrected line
await runTransaction(db, (transaction) =>
updateWithRating(transaction, docRef, newRatingDocument, review),
);
} catch (error) {
console.error(
"There was an error adding the rating to the restaurant",
error,
);
throw error;
}
}
實作 Next.js 伺服器動作
Next.js 伺服器動作提供便利的 API,可存取表單資料,例如 data.get("text"),從表單提交酬載中取得文字值。
如要使用 Next.js 伺服器動作處理評論表單提交作業,請按照下列步驟操作:
- 在
src/components/ReviewDialog.jsx檔案中,找出<form>元素中的action屬性。
<form
action={handleReviewFormSubmission}
onSubmit={() => {
handleClose();
}}
>
action 屬性值是指您在下一個步驟中實作的函式。
- 在
src/app/actions.js檔案中,將handleReviewFormSubmission()函式替換為下列程式碼:
export async function handleReviewFormSubmission(data) {
const { firebaseServerApp } = await getAuthenticatedAppForUser();
const db = getFirestore(firebaseServerApp);
await addReviewToRestaurant(db, data.get("restaurantId"), {
text: data.get("text"),
rating: data.get("rating"),
// This came from a hidden form field.
userId: data.get("userId"),
});
}
新增餐廳評論
您已實作評論提交支援功能,現在可以驗證評論是否正確插入 Cloud Firestore。
如要新增評論並確認評論已插入 Cloud Firestore,請按照下列步驟操作:
- 建立修訂版本,並附上修訂訊息「Allow users to submit restaurant reviews」,然後推送至 GitHub 存放區。
git add . git commit -m "Allow users to submit restaurant reviews" git push - 在 Firebase 控制台中開啟 App Hosting 頁面,等待新的推出作業完成。
- 重新整理網頁應用程式,然後從首頁選取一間餐廳。
- 在餐廳頁面上,按一下
。 - 選取星級評等。
- 撰寫評論。
- 按一下「提交」。你的評論會顯示在評論清單頂端。
- 在 Cloud Firestore 的「新增文件」面板中,搜尋並選取您評論的餐廳文件。
- 在「Start collection」(開始收集) 面板中,選取「ratings」。
- 在「Add document」(新增文件) 面板中,找到要審查的文件,確認文件已正確插入。
9. 儲存使用者從網頁應用程式上傳的檔案
在本節中,您將新增功能,以便在登入後替換與餐廳相關聯的圖片。您將圖片上傳至 Firebase 儲存空間,並在代表餐廳的 Cloud Firestore 文件中更新圖片網址。
如要儲存使用者從網頁應用程式上傳的檔案,請按照下列步驟操作:
- 在
src/components/Restaurant.jsx檔案中,查看使用者上傳檔案時執行的程式碼:
async function handleRestaurantImage(target) {
const image = target.files ? target.files[0] : null;
if (!image) {
return;
}
const imageURL = await updateRestaurantImage(id, image);
setRestaurantDetails({ ...restaurantDetails, photo: imageURL });
}
您不需要對這個函式進行任何變更,但您會在後續步驟中實作 updateRestaurantImage() 函式的行為。
- 在
src/lib/firebase/storage.js檔案中,將updateRestaurantImage()和uploadImage()函式替換為下列程式碼:
export async function updateRestaurantImage(restaurantId, image) {
try {
if (!restaurantId) {
throw new Error("No restaurant ID has been provided.");
}
if (!image || !image.name) {
throw new Error("A valid image has not been provided.");
}
const publicImageUrl = await uploadImage(restaurantId, image);
await updateRestaurantImageReference(restaurantId, publicImageUrl);
return publicImageUrl;
} catch (error) {
console.error("Error processing request:", error);
}
}
async function uploadImage(restaurantId, image) {
const filePath = `images/${restaurantId}/${image.name}`;
const newImageRef = ref(storage, filePath);
await uploadBytesResumable(newImageRef, image);
return await getDownloadURL(newImageRef);
}
系統已為您實作 updateRestaurantImageReference() 函式。這個函式會使用更新後的圖片網址,更新 Cloud Firestore 中的現有餐廳文件。
驗證圖片上傳功能
如要確認圖片是否正常上傳,請按照下列步驟操作:
- 建立修訂版本,並附上修訂訊息「Allow users to change each restaurants' photo」,然後推送至 GitHub 存放區。
git add . git commit -m "Allow users to change each restaurants' photo" git push - 在 Firebase 控制台中開啟 App Hosting 頁面,等待新的推出作業完成。
- 在網頁應用程式中,確認您已登入並選取餐廳。
- 按一下
,然後從檔案系統上傳圖片。圖片會離開本機環境,並上傳至 Cloud Storage。上傳後,圖片會立即顯示。 - 前往 Cloud Storage for Firebase。
- 前往代表餐廳的資料夾。您上傳的圖片已存在於資料夾中。

10. 使用生成式 AI 摘要餐廳評論
在本節中,您將新增評論摘要功能,讓使用者不必逐一閱讀每則評論,就能快速瞭解大家對餐廳的看法。
將 Gemini API 金鑰儲存在 Cloud Secret Manager
- 須有 API 金鑰,才能使用 Gemini API。前往 Google AI Studio,然後點選「建立 API 金鑰」。
- 您可以視需要任意命名金鑰。如果「Choose an imported project」(選擇匯入的專案) 下方未列出您的專案,請按一下「Import project」(匯入專案),在清單中勾選您的專案,然後按一下「Import」(匯入)。最後,在「Choose an imported project」下方選取該專案,然後按一下「Create a key」。
- App Hosting 與 Cloud Secret Manager 整合,可讓您安全儲存 API 金鑰等機密值:
- 在終端機執行下列指令,建立新的 Secret:
firebase apphosting:secrets:set GEMINI_API_KEY- 系統提示輸入密鑰值時,請從 Google AI Studio 複製並貼上 Gemini API 金鑰。
- 當系統詢問新密鑰是用於正式版還是本機測試時,請選擇「正式版」。
- 系統詢問是否要授予存取權,讓後端服務帳戶存取密鑰時,請選取「是」。
- 系統詢問是否要將新的密鑰新增至
apphosting.yaml時,請輸入Y接受。
Gemini API 金鑰現在已安全儲存在 Cloud Secret Manager 中,可供 App Hosting 後端存取。
導入評論摘要元件
- 在
src/components/Reviews/ReviewSummary.jsx中,請使用以下程式碼取代GeminiSummary函式:export async function GeminiSummary({ restaurantId }) { const { firebaseServerApp } = await getAuthenticatedAppForUser(); const reviews = await getReviewsByRestaurantId( getFirestore(firebaseServerApp), restaurantId ); const reviewSeparator = "@"; const prompt = ` Based on the following restaurant reviews, where each review is separated by a '${reviewSeparator}' character, create a one-sentence summary of what people think of the restaurant. Here are the reviews: ${reviews.map((review) => review.text).join(reviewSeparator)} `; try { if (!process.env.GEMINI_API_KEY) { // Make sure GEMINI_API_KEY environment variable is set: // https://genkit.dev/docs/get-started/ throw new Error( 'GEMINI_API_KEY not set. Set it with "firebase apphosting:secrets:set GEMINI_API_KEY"' ); } // Configure a Genkit instance. const ai = genkit({ plugins: [googleAI()], model: gemini20Flash, // set default model }); const { text } = await ai.generate(prompt); return ( <div className="restaurant__review_summary"> <p>{text}</p> <p>✨ Summarized with Gemini</p> </div> ); } catch (e) { console.error(e); return <p>Error summarizing reviews.</p>; } } - 建立修訂版本,並附上修訂訊息「Use AI to summarize reviews」,然後推送至 GitHub 存放區。
git add . git commit -m "Use AI to summarize reviews" git push - 在 Firebase 控制台中開啟 App Hosting 頁面,等待新的推出作業完成。
- 開啟餐廳頁面。頁面頂端會顯示一句話,總結頁面上的所有評論。
- 新增評論並重新整理頁面。摘要應該會隨之變更。
11. 取消發布 App Hosting 網站
完成本程式碼實驗室後,如果不再使用應用程式,可以取消發布,確保沒有人能存取您的 Firestore、Storage 和 Gemini 資源。你隨時可以重新發布。
如要取消發布 App Hosting 網站,請按照下列步驟操作:
- 在 Firebase 控制台中開啟 App Hosting。
- 找出應用程式的後端,然後按一下「View」。
- 在「後端資訊」部分,按一下「網域」旁的「管理」。系統會載入「Domains」頁面。
- 按一下網域旁邊的「更多」圖示 (三個垂直排列的圓點),選擇「停用網域」,然後按一下「停用」確認。
12. 結論
恭喜!您已瞭解如何使用 Firebase 為 Next.js 應用程式新增功能。具體來說,您使用了下列功能:
- Firebase App Hosting:每次推送至設定的分支版本時,自動建構及部署 Next.js 程式碼。
- Firebase 驗證:啟用登入和登出功能。
- Cloud Firestore:用於儲存餐廳資料和餐廳評論資料。
- Cloud Storage for Firebase:用於儲存餐廳圖片。