1. לפני שמתחילים
בקודלאב הזה תלמדו איך לשלב את Firebase עם אפליקציית אינטרנט של Next.js שנקראת Friendly Eats, שהיא אתר לביקורות על מסעדות.
אפליקציית האינטרנט המושלמת כוללת תכונות שימושיות שממחישות איך Firebase יכולה לעזור לכם ליצור אפליקציות Next.js. התכונות האלה כוללות:
- פיתוח ופריסה אוטומטיים: ב-codelab הזה נעשה שימוש ב-Firebase App Hosting כדי לפתח ולפרוס באופן אוטומטי את הקוד של Next.js בכל פעם שמבצעים דחיפה להסתעפות שהוגדרה.
- כניסה ויציאה: אפליקציית האינטרנט המושלמת מאפשרת לכם להיכנס באמצעות חשבון Google ולצאת ממנו. ההתחברות והשמירה של המשתמשים מנוהלים באופן מלא באמצעות אימות ב-Firebase.
- תמונות: אפליקציית האינטרנט המושלמת מאפשרת למשתמשים שמחוברים להעלות תמונות של המסעדה. נכסי התמונות נשמרים ב-Cloud Storage for Firebase. Firebase JavaScript SDK מספק כתובת URL ציבורית לתמונות שהועלו. לאחר מכן, כתובת ה-URL הציבורית הזו מאוחסנת במסמך הרלוונטי של המסעדה ב-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
- סביבת פיתוח עם עורך קוד וטרמינל
- חשבון Google ליצירה ולניהול של פרויקט Firebase
- היכולת לשדרג את פרויקט Firebase לתוכנית התמחור Blaze
2. הגדרת סביבת הפיתוח והמאגר ב-GitHub
בקודלאב הזה נספק את קוד הבסיס של האפליקציה, והוא מבוסס על Firebase CLI.
יצירת מאגר ב-GitHub
המקור של הקודלה נמצא בכתובת https://github.com/firebase/friendlyeats-web. המאגר מכיל פרויקטים לדוגמה לכמה פלטפורמות. עם זאת, ב-codelab הזה נעשה שימוש רק בספרייה 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
- משתמשים בחבילת ה-npm giget כדי לאחזר רק את התיקייה
nextjs-start
:npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
- מעקב אחר שינויים באופן מקומי באמצעות git:
git init git commit -a -m "codelab starting point" git branch -M main
- יוצרים מאגר חדש ב-GitHub: https://github.com/new. נותנים לו שם.
- ב-GitHub תקבלו כתובת URL חדשה של המאגר שנראית כך:
https://github.com/
או/ .git git@github.com:
. מעתיקים את כתובת ה-URL הזו./ .git
- ב-GitHub תקבלו כתובת URL חדשה של המאגר שנראית כך:
- דוחפים את השינויים המקומיים למאגר החדש ב-GitHub. מריצים את הפקודה הבאה, ומחליפים את ערך placeholder
בכתובת ה-URL של המאגר.git remote add origin <your-repository-url> git push -u origin main
- עכשיו הקוד הבסיסי אמור להופיע במאגר שלכם ב-GitHub.
התקנה או עדכון של ה-CLI של Firebase
מריצים את הפקודה הבאה כדי לוודא ש-Firebase CLI מותקן ושהוא בגרסה 13.9.0 ואילך:
firebase --version
אם מופיעה גרסה נמוכה יותר או אם Firebase CLI לא מותקן, מריצים את פקודת ההתקנה:
npm install -g firebase-tools@latest
אם אתם לא מצליחים להתקין את Firebase CLI בגלל שגיאות הרשאה, תוכלו לעיין במסמכי התיעוד של npm או להשתמש באפשרות התקנה אחרת.
התחברות ל-Firebase
- מריצים את הפקודה הבאה כדי להתחבר ל-CLI של Firebase:
firebase login
- מזינים את הערך
Y
אוN
, בהתאם לרצונכם לאסוף נתונים ב-Firebase. - בדפדפן, בוחרים את חשבון Google ולוחצים על אישור.
3. הגדרת פרויקט Firebase
בקטע הזה תגדירו פרויקט Firebase ותשייכו אליו אפליקציית אינטרנט ב-Firebase. בנוסף, תגדירו את שירותי Firebase שבהם משתמשת אפליקציית האינטרנט לדוגמה.
יצירת פרויקט Firebase
- במסוף Firebase, לוחצים על Add project (הוספת פרויקט).
- בתיבת הטקסט Enter your project name, מזינים
FriendlyEats Codelab
(או שם פרויקט אחר לבחירתכם) ולוחצים על Continue. - בחלון הדו-שיח Confirm Firebase billing plan (אישור תוכנית החיוב ב-Firebase), מוודאים שהתוכנית היא Blaze ולוחצים על Confirm plan (אישור התוכנית).
- בקודלאב הזה אין צורך ב-Google Analytics, לכן משביתים את האפשרות Enable Google Analytics for this project (הפעלת Google Analytics בפרויקט הזה).
- לוחצים על Create project.
- ממתינים להקצאת המשאבים לפרויקט ולוחצים על המשך.
- בפרויקט Firebase, עוברים אל Project Settings (הגדרות הפרויקט). חשוב לזכור את מזהה הפרויקט כי תצטרכו אותו בהמשך. המזהה הייחודי הזה משמש לזיהוי הפרויקט (לדוגמה, ב-CLI של Firebase).
שדרוג של תוכנית התמחור ב-Firebase
כדי להשתמש ב-Firebase App Hosting וב-Cloud Storage for Firebase, פרויקט Firebase צריך להיות בתוכנית התמחור 'תשלום לפי שימוש' (Blaze), כלומר הוא צריך להיות מקושר לחשבון לחיוב ב-Cloud.
- בחשבון לחיוב ב-Cloud נדרש אמצעי תשלום, כמו כרטיס אשראי.
- אם אתם משתמשים חדשים ב-Firebase וב-Google Cloud, כדאי לבדוק אם אתם זכאים לקרדיט בסך 300$ולחשבון לחיוב ב-Cloud בתקופת ניסיון בחינם.
- אם אתם מבצעים את הקודלאב הזה כחלק מאירוע, כדאי לשאול את המארגן אם יש זיכויים ב-Cloud שזמינים.
כדי לשדרג את הפרויקט לתוכנית Blaze:
- במסוף Firebase, בוחרים באפשרות שדרוג התוכנית.
- בוחרים את תוכנית Blaze. פועלים לפי ההוראות במסך כדי לקשר חשבון לחיוב ב-Cloud לפרויקט.
אם נדרשת יצירת חשבון לחיוב ב-Cloud כחלק מהשדרוג, יכול להיות שתצטרכו לחזור לתהליך השדרוג במסוף Firebase כדי להשלים את השדרוג.
הוספת אפליקציית אינטרנט לפרויקט Firebase
- עוברים אל סקירה כללית של הפרויקט בפרויקט Firebase, ואז לוחצים על אינטרנט.
אם כבר יש לכם אפליקציות רשומות בפרויקט, לוחצים על הוספת אפליקציה כדי להציג את סמל האינטרנט. - בתיבת הטקסט App nickname (כינוי לאפליקציה), מזינים כינוי לאפליקציה שקל לזכור, כמו
My Next.js app
. - לא מסמנים את התיבה Also set up Firebase Hosting for this app (גם להגדיר אירוח ב-Firebase לאפליקציה הזו).
- לוחצים על Register app (רישום האפליקציה) > Next (הבא) > Next (הבא) > Continue to console (המשך למסוף).
הגדרת שירותי Firebase במסוף Firebase
הגדרת אימות
- במסוף Firebase, עוברים אל Authentication.
- לוחצים על תחילת העבודה.
- בעמודה ספקים נוספים, לוחצים על Google > הפעלה.
- בתיבת הטקסט Public-facing name for project, מזינים שם שקל לזכור, למשל
My Next.js app
. - בתפריט הנפתח Support email for project, בוחרים את כתובת האימייל שלכם.
- לוחצים על שמירה.
הגדרת Cloud Firestore
- בחלונית הימנית של מסוף Firebase, מרחיבים את Build ובוחרים באפשרות Firestore database.
- לוחצים על Create database.
- משאירים את הערך
(default)
בשדה Database ID. - בוחרים מיקום למסד הנתונים ולוחצים על הבא.
באפליקציה אמיתית, כדאי לבחור מיקום קרוב למשתמשים. - לוחצים על התחלה במצב בדיקה. קוראים את כתב הוויתור לגבי כללי האבטחה.
בהמשך הסדנה תוסיפו כללי אבטחה כדי לאבטח את הנתונים. אסור להפיץ או לחשוף אפליקציה באופן ציבורי בלי להוסיף כללי אבטחה למסד הנתונים. - לוחצים על יצירה.
הגדרת Cloud Storage for Firebase
- בחלונית הימנית של מסוף Firebase, מרחיבים את Build ובוחרים באפשרות Storage.
- לוחצים על תחילת העבודה.
- בוחרים מיקום לקטגוריית ברירת המחדל של האחסון.
קטגוריות ב-US-WEST1
, ב-US-CENTRAL1
וב-US-EAST1
יכולות ליהנות מהתוכנית 'תמיד בחינם' ב-Google Cloud Storage. קטגוריות בכל המיקומים האחרים כפופות לתמחור ולשימוש ב-Google Cloud Storage. - לוחצים על התחלה במצב בדיקה. קוראים את כתב הוויתור לגבי כללי האבטחה.
בהמשך הסדנה תוסיפו כללי אבטחה כדי לאבטח את הנתונים. אין להפיץ או לחשוף אפליקציה באופן ציבורי בלי להוסיף כללי אבטחה לקטגוריית האחסון. - לוחצים על יצירה.
4. בדיקת קוד המקור של ערכת ההתחלה
בקטע הזה נבדוק כמה תחומים בקוד הבסיסי של האפליקציה, שאליהם נוסיף פונקציונליות במהלך הקודלאב.
מבנה התיקיות והקבצים
בטבלה הבאה מופיעה סקירה כללית של מבנה התיקיות והקבצים באפליקציה:
תיקיות וקבצים | תיאור |
| רכיבי React למסננים, לכותרות, לפרטי מסעדות ולביקורות |
| פונקציות שירות שלא בהכרח קשורות ל-React או ל-Next.js |
| קוד ותצורה ספציפיים ל-Firebase |
| נכסים סטטיים באפליקציית האינטרנט, כמו סמלים |
| ניתוב באמצעות Next.js App Router |
| טיפול במסלול API |
| יחסי תלות בפרויקט באמצעות npm |
| הגדרה ספציפית ל-Next.js (פעולות השרת מופעלות) |
| הגדרה של שירות שפה של JavaScript |
רכיבים של שרתים ולקוחות
האפליקציה היא אפליקציית אינטרנט של Next.js שמשתמשת ב-App Router. עיבוד בצד השרת משמש בכל האפליקציה. לדוגמה, הקובץ src/app/page.js
הוא רכיב שרת שאחראי לדף הראשי. קובץ src/components/RestaurantListings.jsx
הוא רכיב לקוח שמסומן באמצעות ההנחיה "use client"
בתחילת הקובץ.
ייבוא הצהרות
יכול להיות שתבחינו בהצהרות ייבוא כמו:
import RatingPicker from "@/src/components/RatingPicker.jsx";
האפליקציה משתמשת בסמל @
כדי להימנע מנתיבי ייבוא יחסיים מסורבלים, והיא מתאפשרת באמצעות כינויים לנתיב.
ממשקי API ספציפיים ל-Firebase
כל הקוד של Firebase API ארוז בספרייה src/lib/firebase
. לאחר מכן, רכיבי React נפרדים מייבאים את הפונקציות הארוזות מהספרייה src/lib/firebase
, במקום לייבא פונקציות של Firebase ישירות.
נתוני דמה
נתוני הדמיה של מסעדות וביקורות נכללים בקובץ src/lib/randomData.js
. הנתונים מהקובץ הזה נאספים בקוד בקובץ src/lib/fakeRestaurants.js
.
5. יצירת קצה עורפי של אירוח אפליקציות
בקטע הזה תגדירו קצה עורפי של אירוח אפליקציות כדי לעקוב אחרי הסתעפות במאגר ה-Git.
בסוף הקטע הזה תהיה לכם תשתית לקצה העורפי של אירוח אפליקציות שמחוברת למאגר שלכם ב-GitHub, ותבנה מחדש באופן אוטומטי ותשיק גרסה חדשה של האפליקציה בכל פעם שתדחפו התחייבות חדשה להסתעפות main
.
פריסה של כללי אבטחה
הקוד כבר מכיל קבוצות של כללי אבטחה ל-Firestore ול-Cloud Storage for Firebase. אחרי הפריסה של כללי האבטחה, הנתונים במסד הנתונים ובקטגוריה מוגנים טוב יותר מפני שימוש לרעה.
- בטרמינל, מגדירים את ה-CLI כך שישתמש בפרויקט Firebase שיצרתם מקודם:
firebase use --add
כשמוצגת בקשה להזנת כינוי, מזיניםfriendlyeats-codelab
. - כדי לפרוס את כללי האבטחה האלה, מריצים את הפקודה הבאה בטרמינל:
firebase deploy --only firestore:rules,storage
- אם מוצגת השאלה:
"Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?"
, מקישים עלEnter
כדי לבחור באפשרות כן.
הוספת ההגדרות של Firebase לקוד של אפליקציית האינטרנט
- במסוף Firebase, עוברים אל Project settings.
- בחלונית הגדרה ותצורה של SDK, לוחצים על 'הוספת אפליקציה' ואז על סמל סוגרי הקוד
כדי לרשום אפליקציית אינטרנט חדשה.
- בסוף תהליך היצירה של אפליקציית האינטרנט, מעתיקים את המשתנה
firebaseConfig
ואת המאפיינים שלו ואת הערכים שלהם. - פותחים את הקובץ
apphosting.yaml
בעורך הקוד וממלאים את ערכי משתני הסביבה בערכי התצורה ממסוף Firebase. - מחליפים בקובץ את המאפיינים הקיימים במאפיינים שהעתקתם.
- שומרים את הקובץ.
יצירת קצה עורפי
- עוברים אל הדף App Hosting במסוף Firebase:
- לוחצים על 'תחילת העבודה' כדי להתחיל בתהליך היצירה של הקצה העורפי. מגדירים את הקצה העורפי באופן הבא:
- פועלים לפי ההנחיות בשלב הראשון כדי לחבר את מאגר GitHub שיצרתם מקודם.
- מגדירים את הגדרות הפריסה:
- השארת ספריית השורש כ-
/
- מגדירים את ההסתעפות הפעילה ל-
main
- הפעלת השקות אוטומטיות
- השארת ספריית השורש כ-
- נותנים שם לקצה העורפי
friendlyeats-codelab
. - בקטע 'יצירה או שיוך של אפליקציית אינטרנט ב-Firebase', בוחרים את אפליקציית האינטרנט שהגדרתם מקודם בתפריט הנפתח 'בחירת אפליקציית אינטרנט קיימת ב-Firebase'.
- לוחצים על 'סיום ופריסה'. אחרי רגע, תועברו לדף חדש שבו תוכלו לראות את הסטטוס של הקצה העורפי החדש של אירוח האפליקציות.
- בסיום ההשקה, לוחצים על הדומיין החינמי בקטע 'דומיינים'. יכול להיות שיחלפו כמה דקות עד שהשינוי יתחיל לפעול בגלל הפצת ה-DNS.
פרסתם את אפליקציית האינטרנט הראשונית! בכל פעם שתשלחו גרסה חדשה להטמעה להסתעפות main
במאגר GitHub, תראו שההרצה החדשה וההשקה מתחילות במסוף Firebase, והאתר יתעדכן באופן אוטומטי בסיום ההשקה.
6. הוספת אימות לאפליקציית האינטרנט
בקטע הזה מוסיפים אימות לאפליקציית האינטרנט כדי שתוכלו להתחבר אליה.
הטמעת הפונקציות של הכניסה והיציאה
- בקובץ
src/lib/firebase/auth.js
, מחליפים את הפונקציותonAuthStateChanged
,signInWithGoogle
ו-signOut
בקוד הבא:
export function onAuthStateChanged(cb) {
return _onAuthStateChanged(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);
}
}
הקוד הזה משתמש בממשקי ה-API הבאים של Firebase:
Firebase API | תיאור |
יצירת מכונה של ספק אימות של Google. | |
הפעלת תהליך אימות מבוסס-דיאלוג. | |
יציאה מהחשבון של המשתמש. |
בקובץ src/components/Header.jsx
, הקוד כבר מפעיל את הפונקציות signInWithGoogle
ו-signOut
.
- יוצרים השמירה עם הודעת השמירה 'הוספת אימות Google' ומעבירים אותה למאגר ב-GitHub. 1. פותחים את הדף App Hosting במסוף Firebase וממתינים להשלמת ההשקה החדשה.
- באפליקציית האינטרנט, מרעננים את הדף ולוחצים על כניסה באמצעות חשבון Google. אפליקציית האינטרנט לא מתעדכנת, ולכן לא ברור אם הכניסה בוצעה בהצלחה.
שליחת מצב האימות לשרת
כדי להעביר את מצב האימות לשרת, נשתמש בסוכנות שירות. מחליפים את הפונקציות fetchWithFirebaseHeaders
ו-getAuthIdToken
בקוד הבא:
async function fetchWithFirebaseHeaders(request) {
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const installations = getInstallations(app);
const headers = new Headers(request.headers);
const [authIdToken, installationToken] = await Promise.all([
getAuthIdToken(auth),
getToken(installations),
]);
headers.append("Firebase-Instance-ID-Token", installationToken);
if (authIdToken) headers.append("Authorization", `Bearer ${authIdToken}`);
const newRequest = new Request(request, { headers });
return await fetch(newRequest);
}
async function getAuthIdToken(auth) {
await auth.authStateReady();
if (!auth.currentUser) return;
return await getIdToken(auth.currentUser);
}
קריאת מצב האימות בשרת
נשתמש ב-FirebaseServerApp כדי לשקף את מצב האימות של הלקוח בשרת.
פותחים את src/lib/firebase/serverApp.js
ומחליפים את הפונקציה getAuthenticatedAppForUser
:
export async function getAuthenticatedAppForUser() {
const idToken = headers().get("Authorization")?.split("Bearer ")[1];
console.log('firebaseConfig', JSON.stringify(firebaseConfig));
const firebaseServerApp = initializeServerApp(
firebaseConfig,
idToken
? {
authIdToken: idToken,
}
: {}
);
const auth = getAuth(firebaseServerApp);
await auth.authStateReady();
return { firebaseServerApp, currentUser: auth.currentUser };
}
הרשמה לקבלת עדכונים על שינויים באימות
כדי להירשם לשינויים באימות:
- עוברים לקובץ
src/components/Header.jsx
. - מחליפים את הפונקציה
useUserSession
בקוד הבא:
function useUserSession(initialUser) {
// The initialUser comes from the server via a server component
const [user, setUser] = useState(initialUser);
const router = useRouter();
// Register the service worker that sends auth state back to server
// The service worker is built with npm run build-service-worker
useEffect(() => {
if ("serviceWorker" in navigator) {
const serializedFirebaseConfig = encodeURIComponent(JSON.stringify(firebaseConfig));
const serviceWorkerUrl = `/auth-service-worker.js?firebaseConfig=${serializedFirebaseConfig}`
navigator.serviceWorker
.register(serviceWorkerUrl)
.then((registration) => console.log("scope is: ", registration.scope));
}
}, []);
useEffect(() => {
const unsubscribe = onAuthStateChanged((authUser) => {
setUser(authUser)
})
return () => unsubscribe()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
onAuthStateChanged((authUser) => {
if (user === undefined) return
// refresh when user changed to ease testing
if (user?.email !== authUser?.email) {
router.refresh()
}
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [user])
return user;
}
הקוד הזה משתמש ב-hook של מצב ב-React כדי לעדכן את המשתמש כשהפונקציה onAuthStateChanged
מציינת שיש שינוי במצב האימות.
אימות השינויים
פריסת הבסיס בקובץ src/app/layout.js
מרינדרת את הכותרת ומעבירה את המשתמש, אם הוא זמין, כפרמטר.
<Header initialUser={currentUser?.toJSON()} />
המשמעות היא שהרכיב <Header>
יוצר עיבוד (render) של נתוני משתמשים, אם הם זמינים, במהלך זמן הריצה של השרת. אם יש עדכוני אימות במהלך מחזור החיים של הדף אחרי טעינת הדף הראשונית, ה-handler של onAuthStateChanged
מטפל בהם.
עכשיו הגיע הזמן להשיק גרסה חדשה של build ולאמת את מה שיצרתם.
- יוצרים השמירה עם הודעת השמירה 'הצגת סטטוס הכניסה' ומדחיפים אותה למאגר ב-GitHub.
- פותחים את הדף App Hosting במסוף Firebase וממתינים להשלמת ההשקה החדשה.
- מוודאים את התנהגות האימות החדשה:
- מרעננים את אפליקציית האינטרנט בדפדפן. שם התצוגה מופיע בכותרת.
- יוצאים מהחשבון ונכנסים אליו שוב. הדף מתעדכן בזמן אמת בלי לרענן אותו. אפשר לחזור על השלב הזה עם משתמשים שונים.
- אם רוצים, לוחצים לחיצה ימנית על אפליקציית האינטרנט, בוחרים באפשרות הצגת מקור הדף ומחפשים את השם המוצג. הוא מופיע במקור ה-HTML הגולמי שמוחזר מהשרת.
7. הצגת פרטי המסעדה
אפליקציית האינטרנט כוללת נתונים מדומים של מסעדות וביקורות.
הוספה של מסעדה אחת או יותר
כדי להוסיף נתונים מדומים של מסעדות למסד הנתונים המקומי של Cloud Firestore:
- באפליקציית האינטרנט, בוחרים באפשרות > הוספת מסעדות לדוגמה.
- במסוף 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(),
};
});
}
- יוצרים השמירה עם הודעת השמירה 'קריאת רשימת המסעדות מ-Firestore' ומעבירים אותה למאגר ב-GitHub.
- פותחים את הדף App Hosting במסוף Firebase וממתינים להשלמת ההשקה החדשה.
- באפליקציית האינטרנט, מרעננים את הדף. התמונות של המסעדות מופיעות כמשבצות בדף.
מוודאים שדפי המסעדות נטענים בזמן הריצה של השרת
כשמשתמשים במסגרת Next.js, יכול להיות שלא יהיה ברור מתי הנתונים נטענים בזמן הריצה בשרת או בזמן הריצה בצד הלקוח.
כדי לוודא שדפי המסעדות נטענים בזמן הריצה של השרת, פועלים לפי השלבים הבאים:
- באפליקציית האינטרנט, פותחים את DevTools ומשביתים את JavaScript.
- מרעננים את אפליקציית האינטרנט. רשימות המסעדות עדיין נטענות. פרטי המסעדה מוחזרים בתגובה מהשרת. כש-JavaScript מופעל, פרטי המסעדה מתאוששים באמצעות קוד JavaScript בצד הלקוח.
- ב-DevTools, מפעילים מחדש את JavaScript.
האזנה לעדכונים על מסעדות באמצעות מאזינים לתמונות מצב של Cloud Firestore
בקטע הקודם ראינו איך הקבוצה הראשונית של המסעדות נטענת מקובץ src/app/page.js
. הקובץ src/app/page.js
הוא רכיב שרת, והוא עובר עיבוד בשרת, כולל הקוד של אחזור הנתונים מ-Firebase.
קובץ src/components/RestaurantListings.jsx
הוא רכיב לקוח, וניתן להגדיר אותו כדי להוסיף תוכן לסימני HTML שעבר עיבוד בשרת.
כדי להגדיר את הקובץ src/components/RestaurantListings.jsx
להוספת תוכן לסימון שעבר עיבוד בשרת:
- בקובץ
src/components/RestaurantListings.jsx
, בודקים את הקוד הבא שכבר נכתב בשבילכם:
useEffect(() => {
const unsubscribe = getRestaurantsSnapshot(data => {
setRestaurants(data);
}, filters);
return () => {
unsubscribe();
};
}, [filters]);
הקוד הזה מפעיל את הפונקציה getRestaurantsSnapshot()
, שדומה לפונקציה getRestaurants()
שהטמעתם בשלב קודם. עם זאת, פונקציית snapshot הזו מספקת מנגנון קריאה חוזרת, כך שהקריאה החוזרת מופעלת בכל פעם שמתבצע שינוי באוסף של המסעדה.
- בקובץ
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);
const unsubscribe = 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);
});
return unsubscribe;
}
שינויים שמתבצעים דרך הדף Firestore Database משתקפים עכשיו באפליקציית האינטרנט בזמן אמת.
- יוצרים השמירה עם הודעת השמירה Listen for realtime restaurant updates (האזנה לעדכונים בזמן אמת על מסעדות) ומעבירים אותה למאגר ב-GitHub.
- פותחים את הדף App Hosting במסוף Firebase וממתינים להשלמת ההשקה החדשה.
- באפליקציית האינטרנט, בוחרים באפשרות > הוספת מסעדות לדוגמה. אם הפונקציה של קובץ ה-snapshot מוטמעת בצורה נכונה, המסעדות יופיעו בזמן אמת בלי צורך לרענן את הדף.
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
, מחפשים את המאפייןaction
ברכיב<form>
.
<form action={handleReviewFormSubmission}>
ערך המאפיין action
מתייחס לפונקציה שתטמיעו בשלב הבא.
- בקובץ
src/app/actions.js
, מחליפים את הפונקציהhandleReviewFormSubmission()
בקוד הבא:
// This is a next.js server action, which is an alpha feature, so
// use with caution.
// https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions
export async function handleReviewFormSubmission(data) {
const { app } = await getAuthenticatedAppForUser();
const db = getFirestore(app);
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.
- פותחים את הדף App Hosting במסוף Firebase וממתינים להשלמת ההשקה החדשה.
- מרעננים את אפליקציית האינטרנט ובוחרים מסעדה מדף הבית.
- בדף המסעדה, לוחצים על .
- בוחרים דירוג כוכבים.
- לכתוב ביקורת.
- לוחצים על שליחה. הביקורת תופיע בראש רשימת הביקורות.
- ב-Cloud Firestore, מחפשים את המסמך של המסעדה שבדקתם בחלונית Add document ובוחרים אותו.
- בחלונית Start collection (התחלת האוסף), בוחרים באפשרות ratings (דירוגים).
- בחלונית Add document (הוספת מסמך), מחפשים את המסמך שרוצים לבדוק כדי לוודא שהוא הוכנס כצפוי.
9. שמירת קבצים שהמשתמשים העלו מאפליקציית האינטרנט
בקטע הזה מוסיפים פונקציונליות כדי שתוכלו להחליף את התמונה שמשויכת למסעדה כשאתם מחוברים לחשבון. מעלים את התמונה ל-Firebase Storage ומעדכנים את כתובת ה-URL של התמונה במסמך 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);
setRestaurant({ ...restaurant, 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()
כבר הוטמעה בשבילכם. הפונקציה הזו מעדכנת את כתובת ה-URL של תמונה במסמך קיים של מסעדה ב-Cloud Firestore.
אימות הפונקציונליות של העלאת תמונות
כדי לוודא שהתמונה הועלתה כצפוי, מבצעים את הפעולות הבאות:
- יוצרים השמירה עם הודעת השמירה 'Allow users to change each restaurants' photo' (מתן אפשרות למשתמשים לשנות את התמונה של כל מסעדה) ומעבירים אותה למאגר ב-GitHub.
- פותחים את הדף App Hosting במסוף Firebase וממתינים להשלמת ההשקה החדשה.
- באפליקציית האינטרנט, מוודאים שאתם מחוברים לחשבון ובוחרים מסעדה.
- לוחצים על ומעלים תמונה ממערכת הקבצים. התמונה יוצאת מהסביבה המקומית ומועלה ל-Cloud Storage. התמונה תופיע מיד אחרי שתעלו אותה.
- עוברים אל Cloud Storage ב-Firebase.
- עוברים לתיקייה שמייצגת את המסעדה. התמונה שהעליתם קיימת בתיקייה.
10. סיכום ביקורות על מסעדות באמצעות AI גנרטיבי
בקטע הזה תוסיפו תכונה של סיכום ביקורות, כדי שהמשתמשים יוכלו להבין במהירות מה כולם חושבים על המסעדה בלי לקרוא כל ביקורת.
אחסון מפתח API של Gemini ב-Cloud Secret Manager
- כדי להשתמש ב-Gemini API, אתם צריכים מפתח API. יוצרים מפתח ב-Google AI Studio.
- שירות אירוח האפליקציות משתלב עם Cloud Secret Manager כדי לאפשר לכם לאחסן באופן מאובטח ערכים רגישים כמו מפתחות API:
- בטרמינל, מריצים את הפקודה הבאה כדי ליצור סוד חדש:
firebase apphosting:secrets:set gemini-api-key
- כשמוצגת בקשה להזנת הערך הסודי, מעתיקים את מפתח Gemini API מ-Google AI Studio ומדביקים אותו.
- כשמוצגת השאלה אם להוסיף את הסוד החדש ל-
apphosting.yaml
, מזיניםY
כדי לאשר.
מפתח ה-API של Gemini מאוחסן עכשיו באופן מאובטח ב-Cloud Secret Manager, ויש לו גישה לקצה העורפי של אירוח האפליקציות.
הטמעת הרכיב 'סיכום ביקורות'
- ב-
src/components/Reviews/ReviewSummary.jsx
, מחליפים את הפונקציהGeminiSummary
בקוד הבא:export async function GeminiSummary({ restaurantId }) { const { firebaseServerApp } = await getAuthenticatedAppForUser(); const reviews = await getReviewsByRestaurantId( getFirestore(firebaseServerApp), restaurantId ); const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY); const model = genAI.getGenerativeModel({ model: "gemini-pro"}); 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 { const result = await model.generateContent(prompt); const response = await result.response; const text = response.text(); return ( <div className="restaurant__review_summary"> <p>{text}</p> <p>✨ Summarized with Gemini</p> </div> ); } catch (e) { console.error(e); return <p>Error contacting Gemini</p>; } }
- יוצרים השמירה עם הודעת השמירה 'שימוש ב-AI כדי לסכם ביקורות' ומעבירים אותה למאגר ב-GitHub.
- פותחים את הדף App Hosting במסוף Firebase וממתינים להשלמת ההשקה החדשה.
- פותחים דף של מסעדה. בחלק העליון של הדף אמור להופיע סיכום של כל הביקורות בדף במשפט אחד.
- מוסיפים ביקורת חדשה ומרעננים את הדף. הסיכום אמור להשתנות.
11. סיכום
כל הכבוד! למדתם איך להשתמש ב-Firebase כדי להוסיף תכונות ופונקציות לאפליקציה ב-Next.js. באופן ספציפי, השתמשתם באפשרויות הבאות:
- Firebase App Hosting כדי ליצור ולפרוס באופן אוטומטי את הקוד של Next.js בכל פעם שמבצעים דחיפה להסתעפות מוגדרת.
- אימות ב-Firebase כדי להפעיל את הפונקציונליות של הכניסה והיציאה.
- Cloud Firestore לנתוני מסעדות ולנתוני ביקורות על מסעדות.
- Cloud Storage for Firebase לתמונות של מסעדות.