1. Zanim zaczniesz
Z tego samouczka dowiesz się, jak zintegrować Firebase z aplikacją internetową Next.js o nazwie Friendly Eats, która jest witryną z opiniami o restauracjach.

Gotowa aplikacja internetowa oferuje przydatne funkcje, które pokazują, jak Firebase może pomóc w tworzeniu aplikacji Next.js. Są to:
- Automatyczne kompilowanie i wdrażanie: w tym laboratorium wykorzystujemy Hosting aplikacji Firebase do automatycznego kompilowania i wdrażania kodu Next.js za każdym razem, gdy przenosisz zmiany do skonfigurowanej gałęzi.
- Logowanie i wylogowywanie: ukończona aplikacja internetowa umożliwia logowanie się przez Google i wylogowywanie. Logowanie użytkowników i utrzymywanie sesji jest w całości zarządzane przez Uwierzytelnianie Firebase.
- Obrazy: ukończona aplikacja internetowa umożliwia zalogowanym użytkownikom przesyłanie zdjęć restauracji. Pliki z obrazami są przechowywane w Cloud Storage dla Firebase. Pakiet Firebase JavaScript SDK udostępnia publiczny adres URL przesłanych obrazów. Ten publiczny adres URL jest następnie przechowywany w odpowiednim dokumencie restauracji w Cloud Firestore.
- Opinie: ukończona aplikacja internetowa umożliwia zalogowanym użytkownikom publikowanie opinii o restauracjach, które składają się z oceny w postaci gwiazdek i wiadomości tekstowej. Informacje o opiniach są przechowywane w Cloud Firestore.
- Filtry: ukończona aplikacja internetowa umożliwia zalogowanym użytkownikom filtrowanie listy restauracji na podstawie kategorii, lokalizacji i ceny. Możesz też dostosować używaną metodę sortowania. Dostęp do danych jest uzyskiwany z Cloud Firestore, a zapytania Firestore są stosowane na podstawie użytych filtrów.
Wymagania wstępne
- konto GitHub,
- Znajomość Next.js i JavaScriptu
Czego się nauczysz
- Jak używać Firebase z routerem aplikacji Next.js i renderowaniem po stronie serwera.
- Jak przechowywać obrazy w Cloud Storage dla Firebase.
- Jak odczytywać i zapisywać dane w bazie danych Cloud Firestore.
- Jak korzystać z funkcji Zaloguj się przez Google w pakiecie Firebase JavaScript SDK.
Czego potrzebujesz
- Git
- Najnowsza stabilna wersja Node.js
- wybraną przeglądarkę, np. Google Chrome;
- Środowisko programistyczne z edytorem kodu i terminalem
- konto Google do tworzenia projektu Firebase i zarządzania nim;
- możliwość przejścia na abonament Blaze w projekcie Firebase;
2. Konfigurowanie środowiska programistycznego i repozytorium GitHub
W tym laboratorium znajdziesz początkowy kod aplikacji, który korzysta z interfejsu CLI Firebase.
Tworzenie repozytorium GitHub
Kod źródłowy ćwiczeń z programowania znajdziesz na stronie https://github.com/firebase/friendlyeats-web. Repozytorium zawiera przykładowe projekty na różne platformy. W tym laboratorium używamy jednak tylko katalogu nextjs-start. Zwróć uwagę na te katalogi:
* `nextjs-start`: contains the starter code upon which you build.
* `nextjs-end`: contains the solution code for the finished web app.
Skopiuj folder nextjs-start do własnego repozytorium:
- W terminalu utwórz nowy folder na komputerze i przejdź do nowego katalogu:mkdir codelab-friendlyeats-web cd codelab-friendlyeats-web
- Użyj pakietu npm giget, aby pobrać tylko folder nextjs-start:npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
- Śledź zmiany lokalnie za pomocą Gita:git init git add . git commit -m "codelab starting point" git branch -M main
- Utwórz nowe repozytorium GitHub: https://github.com/new. Nadaj mu dowolną nazwę.
- Skopiuj nowy adres URL utworzony przez GitHub.  Będzie on wyglądać mniej więcej tak:- https://github.com/<USER_NAME>/<REPOSITORY_NAME>.gitlub
- git@github.com:<USER_NAME>/<REPOSITORY_NAME>.git
 
- Wypchnij lokalne zmiany do nowego repozytorium GitHub, wykonując to polecenie. Zastąp symbol zastępczy <REPOSITORY_URL>rzeczywistym adresem URL repozytorium.git remote add origin <REPOSITORY_URL> git push -u origin main
- W repozytorium GitHub powinien pojawić się kod początkowy.
Instalowanie lub aktualizowanie wiersza poleceń Firebase
Uruchom to polecenie, aby sprawdzić, czy wiersz poleceń Firebase jest zainstalowany i czy ma wersję 14.1.0 lub nowszą:
firebase --version
Jeśli widzisz starszą wersję lub nie masz zainstalowanego wiersza poleceń Firebase, uruchom polecenie instalacji:
npm install -g firebase-tools@latest
Jeśli nie możesz zainstalować wiersza poleceń Firebase z powodu błędów uprawnień, zapoznaj się z dokumentacją npm lub skorzystaj z innej opcji instalacji.
Logowanie w Firebase
- Aby zalogować się w interfejsie wiersza poleceń Firebase, uruchom to polecenie:firebase login 
- Wpisz YlubNw zależności od tego, czy chcesz, aby Firebase zbierał dane.
- W przeglądarce wybierz konto Google, a potem kliknij Zezwól.
3. Konfigurowanie projektu Firebase
W tej sekcji skonfigurujesz projekt Firebase i powiążesz z nim aplikację internetową Firebase. Skonfigurujesz też usługi Firebase używane przez przykładową aplikację internetową.
Tworzenie projektu Firebase
- Zaloguj się w konsoli Firebase za pomocą tego samego konta Google, którego używasz w poprzednim kroku.
- Kliknij przycisk, aby utworzyć nowy projekt, a potem wpisz jego nazwę (np. FriendlyEats Codelab).
 
- Kliknij Dalej.
- Po wyświetleniu monitu przeczytaj i zaakceptuj warunki usługi Firebase, a potem kliknij Dalej.
- (Opcjonalnie) Włącz w konsoli Firebase pomoc AI (nazywaną „Gemini w Firebase”).
- W tym samouczku nie potrzebujesz Google Analytics, więc wyłącz opcję Google Analytics.
- Kliknij Utwórz projekt, poczekaj, aż projekt zostanie udostępniony, a następnie kliknij Dalej.
Przejście na wyższy abonament Firebase
Aby korzystać z Hostingu aplikacji Firebase i Cloud Storage for Firebase, Twój projekt Firebase musi być objęty abonamentem Blaze (płatność według wykorzystania), co oznacza, że jest połączony z kontem rozliczeniowym Cloud.
- Konto rozliczeniowe Cloud wymaga formy płatności, np. karty kredytowej.
- Jeśli dopiero zaczynasz korzystać z Firebase i Google Cloud, sprawdź, czy możesz otrzymać środki w wysokości 300 USD i bezpłatne konto rozliczeniowe Cloud.
- Jeśli wykonujesz te ćwiczenia w ramach wydarzenia, zapytaj organizatora, czy są dostępne środki w Google Cloud.
Aby przenieść projekt na abonament Blaze:
- W konsoli Firebase wybierz przejście na wyższy abonament.
- Wybierz pakiet Blaze. Postępuj zgodnie z instrukcjami wyświetlanymi na ekranie, aby połączyć konto rozliczeniowe Cloud z projektem.
 Jeśli w ramach tego przejścia na wyższy abonament konieczne było utworzenie konta rozliczeniowego Cloud, może być konieczne powrócenie do procesu przejścia na wyższy abonament w konsoli Firebase, aby go dokończyć.
Dodawanie aplikacji internetowej do projektu Firebase
- Otwórz Przegląd projektu w projekcie Firebase, a potem kliknij  Internet. Internet.
 Jeśli w projekcie masz już zarejestrowane aplikacje, kliknij Dodaj aplikację, aby zobaczyć ikonę Internet.
- W polu tekstowym Pseudonim aplikacji wpisz łatwy do zapamiętania pseudonim aplikacji, np. My Next.js app.
- Pole wyboru Skonfiguruj też Hosting Firebase dla tej aplikacji pozostaw niezaznaczone.
- Kliknij Zarejestruj aplikację > Przejdź do konsoli.
Konfigurowanie usług Firebase w konsoli Firebase
Konfigurowanie uwierzytelniania
- W konsoli Firebase otwórz Uwierzytelnianie.
- Kliknij Rozpocznij.
- W kolumnie Dodatkowi dostawcy kliknij Google > Włącz.
- W polu tekstowym Nazwa projektu widoczna publicznie wpisz łatwą do zapamiętania nazwę, np. My Next.js app.
- W menu Adres e-mail pomocy dotyczący projektu wybierz swój adres e-mail.
- Kliknij Zapisz.
Konfigurowanie Cloud Firestore
- W panelu po lewej stronie konsoli Firebase rozwiń Kompilacja, a następnie wybierz Baza danych Firestore.
- Kliknij Utwórz bazę danych.
- W polu Identyfikator bazy danych pozostaw wartość (default).
- Wybierz lokalizację bazy danych i kliknij Dalej.
 W przypadku prawdziwej aplikacji wybierz lokalizację, która jest blisko użytkowników.
- Kliknij Uruchom w trybie testowym. Przeczytaj wyłączenie odpowiedzialności dotyczące reguł bezpieczeństwa.
 W dalszej części tego laboratorium dodasz reguły bezpieczeństwa, aby zabezpieczyć swoje dane. Nierozpowszechniajani nie udostępniaj publicznie aplikacji bez dodania reguł bezpieczeństwa do bazy danych.
- Kliknij Utwórz.
Konfigurowanie Cloud Storage dla Firebase
- W panelu po lewej stronie konsoli Firebase rozwiń Kompilacja, a następnie wybierz Storage.
- Kliknij Rozpocznij.
- Wybierz lokalizację domyślnego zasobnika Storage.
 Zasobniki w regionachUS-WEST1,US-CENTRAL1iUS-EAST1mogą korzystać z poziomu „Zawsze bezpłatny” w Google Cloud Storage. W przypadku zasobników w innych lokalizacjach obowiązuje cennik i wykorzystanie Google Cloud Storage.
- Kliknij Uruchom w trybie testowym. Przeczytaj wyłączenie odpowiedzialności dotyczące reguł bezpieczeństwa.
 W dalszej części tego laboratorium dodasz reguły bezpieczeństwa, aby zabezpieczyć swoje dane. Nieudostępniaj aplikacji publicznie bez dodania reguł bezpieczeństwa do zasobnika Storage.
- Kliknij Utwórz.
Wdrażanie reguł zabezpieczeń
Kod zawiera już zestawy reguł zabezpieczeń dla Firestore i Cloud Storage dla Firebase. Po wdrożeniu reguł bezpieczeństwa dane w bazie danych i w zasobniku są lepiej chronione przed niewłaściwym użyciem.
- W terminalu skonfiguruj interfejs CLI do korzystania z utworzonego wcześniej projektu Firebase:firebase use --add friendlyeats-codelab.
- Aby wdrożyć te reguły zabezpieczeń (a także indeksy, które będą potrzebne później), uruchom w terminalu to polecenie:firebase deploy --only firestore,storage 
- Jeśli pojawi się pytanie: "Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?", naciśnijEnter, aby wybrać Tak.
4. Sprawdź początkową bazę kodu
W tej sekcji zapoznasz się z kilkoma obszarami początkowej bazy kodu aplikacji, do których dodasz funkcje w tym laboratorium.
Struktura folderów i plików
Poniższa tabela zawiera omówienie struktury folderów i plików aplikacji:
| Foldery i pliki | Opis | 
| 
 | Komponenty React do filtrów, nagłówków, szczegółów restauracji i opinii | 
| 
 | funkcje narzędziowe, które nie są koniecznie powiązane z Reactem ani Next.js; | 
| 
 | Kod specyficzny dla Firebase i konfiguracja Firebase | 
| 
 | statyczne zasoby w aplikacji internetowej, takie jak ikony; | 
| 
 | Routing za pomocą routera aplikacji Next.js | 
| 
 | Zależności projektu w npm | 
| 
 | Konfiguracja specyficzna dla Next.js (działania serwera są włączone) | 
| 
 | Konfiguracja usługi językowej JavaScript | 
Komponenty serwera i klienta
Aplikacja to aplikacja internetowa Next.js, która korzysta z routera aplikacji. Renderowanie po stronie serwera jest używane w całej aplikacji. Na przykład plik src/app/page.js to komponent serwera odpowiedzialny za stronę główną. Plik src/components/RestaurantListings.jsx to komponent klienta oznaczony dyrektywą "use client" na początku pliku.
Importowanie wyciągów
Możesz zauważyć instrukcje importu podobne do tych:
import RatingPicker from "@/src/components/RatingPicker.jsx";
Aplikacja używa symbolu @, aby uniknąć nieporęcznych względnych ścieżek importu. Jest to możliwe dzięki aliasom ścieżek.
Interfejsy API Firebase
Cały kod interfejsu Firebase API jest zawarty w katalogu src/lib/firebase. Poszczególne komponenty Reacta importują następnie opakowane funkcje z katalogu src/lib/firebase zamiast importować funkcje Firebase bezpośrednio.
Dane testowe
Przykładowe dane restauracji i opinii znajdują się w pliku src/lib/randomData.js. Dane z tego pliku są zbierane w kodzie w pliku src/lib/fakeRestaurants.js.
5. Tworzenie backendu hostingu aplikacji
W tej sekcji skonfigurujesz backend App Hosting, aby obserwować gałąź w repozytorium Git.
Po przeczytaniu tej sekcji będziesz mieć backend App Hosting połączony z repozytorium w GitHubie, który będzie automatycznie ponownie kompilować i wdrażać nową wersję aplikacji za każdym razem, gdy przeniesiesz nowe zatwierdzenie do gałęzi main.
Utworzenie backendu
- Otwórz stronę hostingu aplikacji w konsoli Firebase:

- Aby rozpocząć proces tworzenia backendu, kliknij „Rozpocznij”. Skonfiguruj backend w ten sposób:
- Wybierz region. W przypadku prawdziwej aplikacji wybierz region najbliżej użytkowników.
- Postępuj zgodnie z instrukcjami w kroku „Importowanie repozytorium GitHub”, aby połączyć utworzone wcześniej repozytorium GitHub.
- Ustawienia wdrażania:- Pozostaw katalog główny jako /
- Ustaw gałąź na żywo na main
- Włącz automatyczne wdrażanie
 
- Pozostaw katalog główny jako 
- Nazwij backend friendlyeats-codelab.
- W sekcji „Powiąż aplikację internetową Firebase” kliknij „Utwórz nową aplikację internetową Firebase”.
- Kliknij „Zakończ i wdroż”. Po chwili przejdziesz na nową stronę, na której zobaczysz stan nowego backendu hostingu aplikacji.
- Po zakończeniu wdrażania kliknij bezpłatną domenę w sekcji „Domeny”. Zanim zacznie działać, może minąć kilka minut ze względu na propagację DNS.
- Ojej. Po załadowaniu strony zobaczysz komunikat o błędzie „Błąd aplikacji: wystąpił wyjątek po stronie serwera (więcej informacji znajdziesz w logach serwera)”.
- W konsoli Firebase sprawdź kartę „Dzienniki” zaplecza hostingu aplikacji. Wyświetli się dziennik „Błąd: nie zaimplementowano”. Naprawimy to w następnym kroku, gdy dodamy uwierzytelnianie.
Udało Ci się wdrożyć początkową aplikację internetową. Za każdym razem, gdy przeniesiesz nowe zatwierdzenie do gałęzi main w repozytorium GitHub, w konsoli Firebase zobaczysz nową kompilację i rozpocznie się wdrażanie. Po zakończeniu wdrażania Twoja witryna zostanie automatycznie zaktualizowana.
6. Dodawanie uwierzytelniania do aplikacji internetowej
W tej sekcji dodasz do aplikacji internetowej uwierzytelnianie, aby można było się do niej zalogować.
Dodaj autoryzowaną domenę
Usługa Uwierzytelnianie Firebase będzie akceptować prośby o zalogowanie tylko z dozwolonych domen. Dodamy tu domenę backendu hostingu aplikacji do listy zatwierdzonych domen w Twoim projekcie.
- Skopiuj domenę backendu hostingu aplikacji ze strony „Przegląd” hostingu aplikacji.
- Otwórz kartę Ustawienia uwierzytelniania i wybierz Autoryzowane domeny.
- Kliknij przycisk Dodaj domenę.
- Wpisz domenę backendu hostingu aplikacji.
- Kliknij Dodaj.
Wdrażanie funkcji logowania i wylogowywania
- W pliku src/lib/firebase/auth.jszastąp funkcjeonAuthStateChanged,onIdTokenChanged,signInWithGoogleisignOuttym kodem:
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);
  }
}
Ten kod korzysta z tych interfejsów API Firebase:
| Firebase API | Opis | 
| Dodaje obserwatora zmian stanu logowania użytkownika. | |
| Dodaje obserwatora zmian tokena identyfikacyjnego użytkownika. | |
| Tworzy instancję dostawcy uwierzytelniania Google. | |
| Rozpoczyna proces uwierzytelniania oparty na oknie dialogowym. | |
| Wylogowuje użytkownika. | 
W pliku src/components/Header.jsx kod wywołuje już funkcje signInWithGoogle i signOut.
Wysyłanie stanu uwierzytelniania na serwer
Aby przekazać stan uwierzytelniania na serwer, użyjemy plików cookie. Gdy stan uwierzytelniania w kliencie ulegnie zmianie, zaktualizujemy plik cookie __session.
W pliku src/components/Header.jsx zastąp funkcję useUserSession tym kodem:
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;
}
Odczytywanie stanu uwierzytelniania na serwerze
Użyjemy FirebaseServerApp, aby odzwierciedlić stan uwierzytelniania klienta na serwerze.
Otwórz src/lib/firebase/serverApp.js i zastąp funkcję 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 };
}
Weryfikowanie zmian
Układ główny w pliku src/app/layout.js renderuje nagłówek i przekazuje użytkownika (jeśli jest dostępny) jako właściwość.
<Header initialUser={currentUser?.toJSON()} />
Oznacza to, że komponent <Header> renderuje dane użytkownika (jeśli są dostępne) w czasie działania serwera. Jeśli w trakcie cyklu życia strony po jej początkowym wczytaniu nastąpią jakiekolwiek aktualizacje uwierzytelniania, obsłuży je funkcja onAuthStateChanged.
Teraz nadszedł czas na wdrożenie nowej wersji i sprawdzenie, co zostało utworzone.
- Utwórz zatwierdzenie z komunikatem „Add authentication” (Dodaj uwierzytelnianie) i prześlij je do repozytorium GitHub.
- Otwórz stronę hostingu aplikacji w konsoli Firebase i poczekaj, aż nowe wdrożenie się zakończy.
- Sprawdź nowe zachowanie uwierzytelniania:- Odśwież aplikację internetową w przeglądarce. W nagłówku pojawi się Twoja nazwa wyświetlana.
- Wyloguj się i zaloguj jeszcze raz. Możesz powtórzyć ten krok w przypadku innych użytkowników.
- Opcjonalnie: kliknij prawym przyciskiem myszy aplikację internetową, wybierz Wyświetl źródło strony i wyszukaj wyświetlaną nazwę. Pojawia się w surowym kodzie HTML zwróconym z serwera.
 
7. Wyświetlanie informacji o restauracji
Aplikacja internetowa zawiera przykładowe dane dotyczące restauracji i opinii.
Dodawanie co najmniej 1 restauracji
Aby wstawić przykładowe dane restauracji do lokalnej bazy danych Cloud Firestore, wykonaj te czynności:
- Zaloguj się w aplikacji internetowej, jeśli jeszcze tego nie zrobiono. Następnie kliknij  > Dodaj przykładowe restauracje. > Dodaj przykładowe restauracje.
- W konsoli Firebase na stronie Baza danych Firestore kliknij restaurants. Widzisz dokumenty najwyższego poziomu documents w kolekcji restauracji, z których każdy reprezentuje jedną restaurację.
- Kliknij kilka dokumentów, aby poznać właściwości dokumentu restauracji.
Wyświetlanie listy restauracji
W bazie danych Cloud Firestore znajdują się teraz restauracje, które aplikacja internetowa Next.js może wyświetlać.
Aby zdefiniować kod pobierania danych:
- W pliku src/app/page.jsznajdź komponent serwera<Home />i sprawdź wywołanie funkcjigetRestaurants, która pobiera listę restauracji w czasie działania serwera. FunkcjęgetRestaurantszaimplementujesz w kolejnych krokach.
- W pliku src/lib/firebase/firestore.jszastąp funkcjeapplyQueryFiltersigetRestaurantstym kodem:
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(),
    };
  });
}
- Utwórz zatwierdzenie z komunikatem „Read the list of restaurants from Firestore” (Odczytaj listę restauracji z Firestore) i prześlij je do repozytorium GitHub.
- Otwórz stronę hostingu aplikacji w konsoli Firebase i poczekaj, aż nowe wdrożenie się zakończy.
- W aplikacji internetowej odśwież stronę. Zdjęcia restauracji pojawiają się na stronie w postaci kafelków.
Sprawdź, czy informacje o restauracjach wczytują się w czasie działania serwera.
W przypadku korzystania z platformy Next.js może nie być oczywiste, kiedy dane są wczytywane w czasie działania serwera lub klienta.
Aby sprawdzić, czy wizytówki restauracji wczytują się w czasie działania serwera, wykonaj te czynności:
- W aplikacji internetowej otwórz Narzędzia deweloperskie i wyłącz JavaScript.

- Odśwież aplikację internetową. Listy restauracji nadal się wczytują. Informacje o restauracji są zwracane w odpowiedzi serwera. Gdy JavaScript jest włączony, informacje o restauracji są wypełniane za pomocą kodu JavaScript po stronie klienta.
- W Narzędziach deweloperskich ponownie włącz JavaScript.
Nasłuchiwanie aktualizacji restauracji za pomocą detektorów zrzutów Cloud Firestore
W poprzedniej sekcji pokazaliśmy, jak początkowy zestaw restauracji został wczytany z pliku src/app/page.js. Plik src/app/page.js jest komponentem serwera i jest renderowany na serwerze, w tym kod pobierania danych Firebase.
Plik src/components/RestaurantListings.jsx jest komponentem klienta i można go skonfigurować tak, aby uzupełniał znaczniki renderowane na serwerze.
Aby skonfigurować plik src/components/RestaurantListings.jsx do wypełniania znaczników renderowanych po stronie serwera, wykonaj te czynności:
- W pliku src/components/RestaurantListings.jsxzwróć uwagę na ten kod, który jest już napisany:
useEffect(() => {
    return getRestaurantsSnapshot((data) => {
      setRestaurants(data);
    }, filters);
  }, [filters]);
Ten kod wywołuje funkcję getRestaurantsSnapshot(), która jest podobna do funkcji getRestaurants() zaimplementowanej w poprzednim kroku. Ta funkcja zrzutu udostępnia jednak mechanizm wywołania zwrotnego, dzięki czemu wywołanie zwrotne jest wywoływane za każdym razem, gdy w kolekcji restauracji wprowadzana jest zmiana.
- W pliku src/lib/firebase/firestore.jszastąp funkcjęgetRestaurantsSnapshot()tym kodem:
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);
  });
}
Zmiany wprowadzone na stronie Baza danych Firestore są teraz odzwierciedlane w aplikacji internetowej w czasie rzeczywistym.
- Utwórz zatwierdzenie z komunikatem „Listen for realtime restaurant updates” (Nasłuchiwanie aktualizacji restauracji w czasie rzeczywistym) i prześlij je do repozytorium GitHub.
- Otwórz stronę hostingu aplikacji w konsoli Firebase i poczekaj, aż nowe wdrożenie się zakończy.
- W aplikacji internetowej kliknij  > Dodaj przykładowe restauracje. Jeśli funkcja migawki jest prawidłowo wdrożona, restauracje będą się pojawiać w czasie rzeczywistym bez odświeżania strony. > Dodaj przykładowe restauracje. Jeśli funkcja migawki jest prawidłowo wdrożona, restauracje będą się pojawiać w czasie rzeczywistym bez odświeżania strony.
8. Zapisywanie opinii przesłanych przez użytkowników z aplikacji internetowej
- W pliku src/lib/firebase/firestore.jszastąp funkcjęupdateWithRating()tym kodem:
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()),
  });
};
Ten kod wstawia nowy dokument Firestore reprezentujący nową opinię. Kod aktualizuje też istniejący dokument Firestore, który reprezentuje restaurację, o zaktualizowane dane dotyczące liczby ocen i średniej obliczonej oceny.
- Zastąp funkcję addReviewToRestaurant()tym kodem:
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;
	}
}
Implementowanie działania serwera Next.js
Działanie serwera Next.js udostępnia wygodny interfejs API do uzyskiwania dostępu do danych formularza, np. data.get("text"), aby pobrać wartość tekstową z ładunku przesłanego formularza.
Aby przetworzyć przesłany formularz opinii za pomocą działania serwera Next.js, wykonaj te czynności:
- W pliku src/components/ReviewDialog.jsxznajdź atrybutactionw elemencie<form>.
<form action={handleReviewFormSubmission}>
Wartość atrybutu action odnosi się do funkcji, którą zaimplementujesz w następnym kroku.
- W pliku src/app/actions.jszastąp funkcjęhandleReviewFormSubmission()tym kodem:
// 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"),
        });
}
Dodawanie opinii o restauracji
Wdrożono obsługę przesyłania opinii, więc teraz możesz sprawdzić, czy opinie są prawidłowo wstawiane do Cloud Firestore.
Aby dodać opinię i sprawdzić, czy została wstawiona do Cloud Firestore, wykonaj te czynności:
- Utwórz zatwierdzenie z komunikatem „Allow users to submit restaurant reviews” (Zezwól użytkownikom na przesyłanie opinii o restauracjach) i prześlij je do repozytorium GitHub.
- Otwórz stronę hostingu aplikacji w konsoli Firebase i poczekaj, aż nowe wdrożenie się zakończy.
- Odśwież aplikację internetową i wybierz restaurację na stronie głównej.
- Na stronie restauracji kliknij  . .
- Wybierz liczbę gwiazdek.
- Napisz opinię.
- Kliknij Prześlij. Twoja opinia pojawi się u góry listy opinii.
- W Cloud Firestore w panelu Dodaj dokument wyszukaj i wybierz dokument restauracji, którą oceniasz.
- W panelu Rozpocznij zbieranie kliknij oceny.
- W panelu Dodaj dokument znajdź dokument do sprawdzenia, aby upewnić się, że został wstawiony zgodnie z oczekiwaniami.

9. Zapisywanie plików przesłanych przez użytkowników z aplikacji internetowej
W tej sekcji dodasz funkcję, która umożliwi zastąpienie obrazu powiązanego z restauracją po zalogowaniu się. Prześlij obraz do Firebase Storage i zaktualizuj adres URL obrazu w dokumencie Cloud Firestore, który reprezentuje restaurację.
Aby zapisać pliki przesłane przez użytkowników z aplikacji internetowej, wykonaj te czynności:
- W pliku src/components/Restaurant.jsxsprawdź kod, który jest uruchamiany, gdy użytkownik przesyła plik:
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 });
}
Nie musisz wprowadzać w tej funkcji żadnych zmian, ale w kolejnych krokach zaimplementujesz działanie funkcji updateRestaurantImage().
- W pliku src/lib/firebase/storage.jszastąp funkcjeupdateRestaurantImage()iuploadImage()tym kodem:
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);
}
Funkcja updateRestaurantImageReference() jest już wdrożona. Ta funkcja aktualizuje istniejący dokument restauracji w Cloud Firestore, dodając do niego zaktualizowany adres URL obrazu.
Sprawdź funkcję przesyłania obrazów.
Aby sprawdzić, czy obraz przesyła się zgodnie z oczekiwaniami, wykonaj te czynności:
- Utwórz zatwierdzenie z komunikatem „Allow users to change each restaurants' photo” (Zezwól użytkownikom na zmianę zdjęcia każdej restauracji) i prześlij je do repozytorium GitHub.
- Otwórz stronę hostingu aplikacji w konsoli Firebase i poczekaj, aż nowe wdrożenie się zakończy.
- W aplikacji internetowej sprawdź, czy jesteś zalogowany(-a), i wybierz restaurację.
- Kliknij  i prześlij obraz z systemu plików. Obraz opuszcza środowisko lokalne i jest przesyłany do Cloud Storage. Obraz pojawi się natychmiast po przesłaniu. i prześlij obraz z systemu plików. Obraz opuszcza środowisko lokalne i jest przesyłany do Cloud Storage. Obraz pojawi się natychmiast po przesłaniu.
- Otwórz Cloud Storage dla Firebase.
- Otwórz folder reprezentujący restaurację. Przesłany obraz znajduje się w folderze.

10. Podsumowywanie opinii o restauracjach za pomocą generatywnej AI
W tej sekcji dodasz funkcję podsumowania opinii, dzięki której użytkownik będzie mógł szybko sprawdzić, co myślą o restauracji inni, bez konieczności czytania każdej opinii.
Przechowywanie klucza interfejsu Gemini API w usłudze Cloud Secret Manager
- Aby korzystać z interfejsu Gemini API, musisz mieć klucz interfejsu API. Otwórz Google AI Studio i kliknij „Utwórz klucz interfejsu API”.
- W polu „Wyszukaj projekty Google Cloud” wybierz projekt Firebase. Każdy projekt Firebase jest oparty na projekcie Google Cloud.
- App Hosting jest zintegrowany z Cloud Secret Manager, co umożliwia bezpieczne przechowywanie wartości wrażliwych, takich jak klucze interfejsu API:- W terminalu uruchom polecenie, aby utworzyć nowy obiekt tajny:
 firebase apphosting:secrets:set GEMINI_API_KEY- Gdy pojawi się prośba o podanie wartości tajnej, skopiuj i wklej klucz interfejsu Gemini API z Google AI Studio.
- Gdy pojawi się pytanie, czy nowy klucz tajny jest przeznaczony do środowiska produkcyjnego czy testów lokalnych, wybierz „Środowisko produkcyjne”.
- Gdy pojawi się pytanie, czy chcesz przyznać dostęp, aby konto usługi backendu mogło uzyskać dostęp do klucza tajnego, wybierz „Tak”.
- Gdy pojawi się pytanie, czy nowy klucz tajny ma zostać dodany do apphosting.yaml, wpiszY, aby zaakceptować.
 
Klucz interfejsu Gemini API jest teraz bezpiecznie przechowywany w usłudze Cloud Secret Manager i jest dostępny dla backendu App Hosting.
Wdrażanie komponentu podsumowania opinii
- W pliku src/components/Reviews/ReviewSummary.jsxzastąp funkcjęGeminiSummarytym kodem: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://firebase.google.com/docs/genkit/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>; } }
- Utwórz zatwierdzenie z komunikatem „Use AI to summarize reviews” (Użyj AI do podsumowywania opinii) i prześlij je do repozytorium GitHub.
- Otwórz stronę hostingu aplikacji w konsoli Firebase i poczekaj, aż nowe wdrożenie się zakończy.
- Otwórz stronę restauracji. U góry powinna być widoczna podsumowująca wszystkie opinie na stronie.
- Dodaj nową opinię i odśwież stronę. Powinno pojawić się podsumowanie zmiany.
11. Podsumowanie
Gratulacje! Dowiedzieliśmy się, jak używać Firebase do dodawania funkcji i funkcjonalności do aplikacji Next.js. W szczególności użyliśmy tych usług:
- Firebase App Hosting, aby automatycznie tworzyć i wdrażać kod Next.js za każdym razem, gdy przenosisz go do skonfigurowanej gałęzi.
- Uwierzytelnianie Firebase, aby włączyć funkcje logowania i wylogowywania.
- Cloud Firestore na potrzeby danych restauracji i opinii o restauracjach.
- Cloud Storage dla Firebase na potrzeby zdjęć restauracji.