Zintegruj Firebase z aplikacją Next.js

1. Zanim zaczniesz

Z tego Codelab dowiesz się, jak zintegrować Firebase z aplikacją internetową Next.js o nazwie Friendly Eats, która jest stroną z opiniami o restauracjach.

Aplikacja internetowa Friendly Eats

Ukończona aplikacja internetowa zawiera przydatne funkcje, które pokazują, jak Firebase może Ci pomóc w tworzeniu aplikacji Next.js. Do tych funkcji należą:

  • Automatyczne kompilowanie i wdrażanie: ten warsztat z kodowaniem korzysta z Firebase App Hosting, aby automatycznie kompilować i wdrażać kod Next.js za każdym razem, gdy przenosisz go do skonfigurowanej gałęzi.
  • Logowanie się i wylogowywanie: gotowa aplikacja internetowa umożliwia logowanie się przez Google i wylogowywanie. Logowaniem i trwałością użytkowników zarządza się wyłącznie za pomocą Uwierzytelniania Firebase.
  • Zdjęcia: gotowa aplikacja internetowa umożliwia zalogowanym użytkownikom przesyłanie zdjęć restauracji. Zasoby obrazów są przechowywane w Cloud Storage dla Firebase. Pakiet Firebase JavaScript SDK udostępnia publiczny URL przesłanych obrazów. Ten publiczny adres URL jest następnie przechowywany w dokumentach dotyczących danej restauracji w Cloud Firestore.
  • Opinie: gotowa aplikacja internetowa umożliwia zalogowanym użytkownikom publikowanie opinii o restauracjach, które składają się z oceny w gwiazdkach i wiadomości tekstowej. Informacje o recenzjach są przechowywane w Cloud Firestore.
  • Filtry: gotowa aplikacja internetowa umożliwia zalogowanym użytkownikom filtrowanie listy restauracji według kategorii, lokalizacji i ceny. Możesz też dostosować używaną metodę sortowania. Dane są pobierane z Cloud Firestore, a zapytania do Firestore są stosowane na podstawie użytych filtrów.

Wymagania wstępne

  • Konto GitHub
  • znajomość Next.js i JavaScriptu;

Czego się nauczysz

  • Jak korzystać z Firebase z platformą Next.js App Router i renderowaniem po stronie serwera.
  • Jak zapisywać obrazy w Cloud Storage dla Firebase.
  • Jak odczytywać i zapisywać dane w bazie danych Cloud Firestore.
  • Jak korzystać z funkcji logowania się przez Google w pakiecie Firebase JavaScript SDK.

Czego potrzebujesz

  • Git
  • najnowsza stabilna wersja Node.js,
  • 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

Ten warsztat programistyczny zawiera podstawową bazę kodu aplikacji i korzysta z wiersza poleceń Firebase.

Tworzenie repozytorium GitHub

Źródło kodu znajdziesz na stronie https://github.com/firebase/friendlyeats-web. Repozytorium zawiera przykładowe projekty na wiele platform. Ten kodlab używa 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 swojego repozytorium:

  1. Za pomocą terminala utwórz nowy folder na komputerze i przejdź do nowego katalogu:
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. Aby pobrać tylko folder nextjs-start, użyj pakietu npm giget:
    npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
    
  3. Śledź zmiany lokalnie za pomocą git:
    git init
    
    git commit -a -m "codelab starting point"
    
    git branch -M main
    
  4. Utwórz nowe repozytorium GitHub: https://github.com/new. Możesz nadać mu dowolną nazwę.
    1. GitHub poda Ci nowy adres URL repozytorium, który wygląda tak: https://github.com//.git lub git@github.com:/.git. Skopiuj ten adres URL.
  5. Prześlij zmiany lokalne do nowego repozytorium GitHub. Uruchom to polecenie, zastępując miejsce wskaźnik zastępczy URL repozytorium.
    git remote add origin <your-repository-url>
    
    git push -u origin main
    
  6. W repozytorium GitHub powinien teraz być widoczny kod startowy.

Instalowanie lub aktualizowanie wiersza poleceń Firebase

Uruchom to polecenie, aby sprawdzić, czy masz zainstalowany interfejs wiersza poleceń Firebase w wersji 13.9.0 lub nowszej:

firebase --version

Jeśli widzisz niższą wersję lub wiersz poleceń Firebase nie jest zainstalowany, 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 użyj innej opcji instalacji.

Logowanie się w Firebase

  1. Aby zalogować się w wierszu poleceń Firebase, uruchom to polecenie:
    firebase login
    
  2. Wpisz Y lub N w zależności od tego, czy chcesz, aby Firebase zbierała dane.
  3. W przeglądarce wybierz konto Google, a potem kliknij Zezwól.

3. Konfigurowanie projektu Firebase

W tej sekcji skonfigurujesz projekt Firebase i połączysz z nim aplikację internetową Firebase. Skonfigurujesz też usługi Firebase używane przez przykładową aplikację internetową.

Tworzenie projektu Firebase

  1. W konsoli Firebase kliknij Dodaj projekt.
  2. W polu tekstowym Wpisz nazwę projektu wpisz FriendlyEats Codelab (lub dowolną nazwę projektu), a potem kliknij Dalej.
  3. W oknie Potwierdź plan płatności Firebase sprawdź, czy wybrany plan to Blaze, a potem kliknij Potwierdź plan.
  4. W tym ćwiczeniu nie potrzebujesz Google Analytics, więc wyłącz opcję Włącz Google Analytics w tym projekcie.
  5. Kliknij Utwórz projekt.
  6. Poczekaj, aż projekt zostanie przygotowany, a potem kliknij Dalej.
  7. W projekcie Firebase otwórz Ustawienia projektu. Zapisz identyfikator projektu, bo będzie Ci on potrzebny później. Ten unikalny identyfikator służy do identyfikowania Twojego projektu (np. w wierszu poleceń Firebase).

Przejdź na wyższy abonament Firebase

Aby korzystać z hostingu aplikacji i Cloud Storage w Firebase, musisz mieć projekt Firebase w abonamentie płatność według wykorzystania (Blaze), co oznacza, że jest on połączony z kontem płatności Google Cloud.

Aby przenieść projekt na abonament Blaze:

  1. W konsoli Firebase wybierz uaktualnienie abonamentu.
  2. 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 poziom abonamentu musisz utworzyć konto rozliczeniowe Cloud, konieczne może być powrót do procesu przejścia w konsoli Firebase, aby go dokończyć.

Dodawanie aplikacji internetowej do projektu Firebase

  1. W swoim projekcie Firebase otwórz stronę Przegląd projektu, a potem kliknij e41f2efdd9539c31.png Internet.

    Jeśli w projekcie masz już zarejestrowane aplikacje, kliknij Dodaj aplikację, aby wyświetlić ikonę Internetu.
  2. W polu tekstowym Nazwa skrótowa aplikacji wpisz zapadającą w pamięć nazwę skrótową aplikacji, np. My Next.js app.
  3. Nie zaznaczaj pola wyboru Skonfiguruj również Hosting Firebase dla tej aplikacji.
  4. Kliknij Zarejestruj aplikację > Dalej > Dalej > Kontynuuj w konsoli.

Konfigurowanie usług Firebase w konsoli Firebase

Konfigurowanie uwierzytelniania

  1. W konsoli Firebase otwórz sekcję Uwierzytelnianie.
  2. Kliknij Rozpocznij.
  3. W kolumnie Dodatkowi dostawcy kliknij Google > Włącz.
  4. W polu tekstowym Nazwa projektu widoczna publicznie wpisz zapadającą w pamięć nazwę, np. My Next.js app.
  5. W menu Adres e-mail pomocy dla projektu wybierz swój adres e-mail.
  6. Kliknij Zapisz.

Konfigurowanie Cloud Firestore

  1. W panelu po lewej stronie w konsoli Firebase rozwiń Kompilacja, a potem wybierz Baza danych Firestore.
  2. Kliknij Utwórz bazę danych.
  3. Pozostaw wartość (default) w polu Identyfikator bazy danych.
  4. Wybierz lokalizację bazy danych, a potem kliknij Dalej.
    W przypadku prawdziwej aplikacji wybierz lokalizację blisko użytkowników.
  5. Kliknij Rozpocznij w trybie testowym. Przeczytaj wyłączenie odpowiedzialności dotyczące reguł bezpieczeństwa.
    W dalszej części tego Codelab dodasz reguły bezpieczeństwa, aby chronić swoje dane. Nie udostępniaj ani nie udostępniaj publicznie aplikacji bez dodania reguł bezpieczeństwa dla bazy danych.
  6. Kliknij Utwórz.

Konfigurowanie Cloud Storage dla Firebase

  1. W panelu po lewej stronie konsoli Firebase rozwiń Kompilacja, a potem kliknij Storage.
  2. Kliknij Rozpocznij.
  3. Wybierz lokalizację domyślnego zasobnika Storage.
    Zasobniki w regionach US-WEST1, US-CENTRAL1 i US-EAST1 mogą korzystać z poziomu Always Free w Google Cloud Storage. Zasobniki w innych lokalizacjach podlegają cennikom i zasadom korzystania z Google Cloud Storage.
  4. Kliknij Rozpocznij w trybie testowym. Przeczytaj wyłączenie odpowiedzialności dotyczące reguł bezpieczeństwa.
    W dalszej części tego Codelab dodasz reguły bezpieczeństwa, które ochronią Twoje dane. Nie udostępniaj ani nie udostępniaj publicznie aplikacji bez dodania reguł bezpieczeństwa dla zasobnika Storage.
  5. Kliknij Utwórz.

4. Sprawdzanie kodu źródłowego bazy danych

W tej sekcji przyjrzysz się kilku obszarom podstawowego kodu źródłowego aplikacji, do których w ramach tego Codelab dodasz funkcje.

Struktura folderów i plików

W tabeli poniżej znajdziesz omówienie struktury folderów i plików aplikacji:

Foldery i pliki

Opis

src/components

Komponenty React do filtrów, nagłówków, szczegółów restauracji i opinii

src/lib

funkcje pomocnicze, które nie są koniecznie powiązane z React lub Next.js;

src/lib/firebase

Kod i konfiguracja Firebase

public

komponenty statyczne w aplikacji internetowej, np. ikony;

src/app

Przekierowywanie za pomocą routera aplikacji Next.js

src/app/restaurant

Obsługa trasy interfejsu API

package.json i package-lock.json

Zależności projektu w npm

next.config.js

Konfiguracja specyficzna dla Next.js (czynności serwera są włączone)

jsconfig.json

Konfiguracja usługi językowej JavaScript

Komponenty serwera i klienta

Aplikacja jest aplikacją internetową Next.js, która korzysta z App Router. Renderowanie po stronie serwera jest używane w całej aplikacji. Na przykład plik src/app/page.js to element 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 tej:

import RatingPicker from "@/src/components/RatingPicker.jsx";

Aplikacja używa symbolu @, aby uniknąć nieporęcznych ścieżek importu względnego. Jest to możliwe dzięki aliasom ścieżek.

Interfejsy API specyficzne dla Firebase

Cały kod interfejsu API Firebase jest zapakowany w katalogu src/lib/firebase. Poszczególne komponenty Reacta importują następnie owinięte funkcje z katalogu src/lib/firebase, a nie importują bezpośrednio funkcji Firebase.

Dane testowe

Plik src/lib/randomData.js zawiera dane o fikcyjnych restauracjach i recenzjach. Dane z tego pliku są łączone w kodzie w pliku src/lib/fakeRestaurants.js.

5. Tworzenie backendu hostingu aplikacji

W tej sekcji skonfigurujesz backend usługi tworzenia aplikacji, aby obserwować gałąź w repozytorium Git.

Po zakończeniu tej sekcji będziesz mieć backend usługi hostingu aplikacji połączony z Twoim repozytorium w GitHub, który automatycznie będzie ponownie kompilować i wdrażać nową wersję aplikacji za każdym razem, gdy prześlesz nowe zatwierdzenie do gałęzi main.

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 pojemniku są lepiej chronione przed niewłaściwym użyciem.

  1. W terminalu skonfiguruj CLI do korzystania z utworzonego wcześniej projektu Firebase:
    firebase use --add
    
    Gdy pojawi się prośba o alias, wpisz friendlyeats-codelab.
  2. Aby wdrożyć te reguły zabezpieczeń, uruchom w terminalu to polecenie:
    firebase deploy --only firestore:rules,storage
    
  3. Jeśli pojawi się pytanie: "Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?", naciśnij Enter, aby wybrać Tak.

Dodawanie konfiguracji Firebase do kodu aplikacji internetowej

  1. W konsoli Firebase otwórz Ustawienia projektu.
  2. Aby zarejestrować nową aplikację internetową, w panelu Konfiguracja i ustawienie SDK kliknij „Dodaj aplikację” i ikonę klamer kodowych .
  3. Pod koniec procesu tworzenia aplikacji internetowej skopiuj zmienną firebaseConfig oraz jej właściwości i wartości.
  4. Otwórz plik apphosting.yaml w edytorze kodu i wpisz wartości zmiennych środowiskowych, używając wartości konfiguracji z konsoli Firebase.
  5. W pliku zastąp istniejące właściwości tymi, które skopiowałeś.
  6. Zapisz plik.

Utworzenie backendu

  1. Otwórz w konsoli Firebase stronę Hosting aplikacji:

Pusty stan konsoli hostingu aplikacji z przyciskiem „Rozpocznij”

  1. Aby rozpocząć proces tworzenia backendu, kliknij „Rozpocznij”. Skonfiguruj backend w ten sposób:
  2. Postępuj zgodnie z instrukcjami wyświetlanymi w pierwszym kroku, aby połączyć utworzone wcześniej repozytorium GitHub.
  3. Ustaw ustawienia wdrażania:
    1. Zachowaj katalog główny jako /
    2. Ustaw aktywną gałąź na main
    3. Włączanie automatycznego wdrażania
  4. Nazwij backend friendlyeats-codelab.
  5. W sekcji „Utwórz lub powiązaj aplikację internetową Firebase” wybierz skonfigurowaną wcześniej aplikację internetową z menu „Wybierz istniejącą aplikację internetową Firebase”.
  6. Kliknij „Zakończ i wdróż”. Po chwili przeniesiesz się na nową stronę, na której możesz sprawdzić stan nowego backendu usługi Hostowanie aplikacji.
  7. Po zakończeniu wdrażania kliknij swoją bezpłatną domenę w sekcji „Domeny”. Z powodu propagacji DNS może minąć kilka minut, zanim zmiany zaczną działać.

Udało Ci się wdrożyć początkową aplikację internetową. Za każdym razem, gdy wypychasz nowe zatwierdzenie do gałęzi main w repozytorium GitHub, w konsoli Firebase rozpoczyna się nowe kompilowanie i wdrażanie. Po zakończeniu wdrażania Twoja witryna zostanie automatycznie zaktualizowana.

6. Dodawanie uwierzytelniania do aplikacji internetowej

W tej sekcji dodasz uwierzytelnianie do aplikacji internetowej, aby móc się w niej zalogować.

Implementowanie funkcji logowania i wylogowywania

  1. W pliku src/lib/firebase/auth.js zastąp funkcje onAuthStateChanged, signInWithGooglesignOut tym kodem:
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);
  }
}

Ten kod korzysta z tych interfejsów API Firebase:

Firebase API

Opis

GoogleAuthProvider

Tworzy instancję dostawcy uwierzytelniania Google.

signInWithPopup

Rozpoczyna proces uwierzytelniania za pomocą dialogu.

auth.signOut

Wyloguj użytkownika.

W pliku src/components/Header.jsx kod już wywołuje funkcje signInWithGooglesignOut.

  1. Utwórz zatwierdzenie z komunikatem „Dodawanie uwierzytelniania Google” i prześlij je do repozytorium GitHub. 1. W konsoli Firebase otwórz stronę Hosting aplikacji i poczekaj na zakończenie nowego wdrożenia.
  2. W aplikacji internetowej odśwież stronę i kliknij Zaloguj się przez Google. Aplikacja internetowa się nie aktualizuje, więc nie wiadomo, czy udało się zalogować.

Wysyłanie stanu uwierzytelniania na serwer

Aby przekazać stan uwierzytelniania na serwer, użyjemy usługa robocza. Zastąp funkcje fetchWithFirebaseHeadersgetAuthIdToken tym kodem:

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);
}

Odczyt stanu uwierzytelniania na serwerze

Użyjemy FirebaseServerApp, aby odzwierciedlić stan uwierzytelniania klienta na serwerze.

Otwórz funkcję src/lib/firebase/serverApp.js i zastąp ją funkcją 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 };
}

Subskrypcja zmian dotyczących uwierzytelniania

Aby subskrybować zmiany dotyczące uwierzytelniania:

  1. Przejdź do pliku src/components/Header.jsx.
  2. Zastąp funkcję useUserSession tym kodem:
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;
}

Ten kod używa haku state w React, aby aktualizować użytkownika, gdy funkcja onAuthStateChanged określi, że nastąpiła zmiana stanu uwierzytelniania.

Weryfikowanie zmian

Układ główny w pliku src/app/layout.js renderuje nagłówek i przekazuje użytkownika jako argument (jeśli jest dostępny).

<Header initialUser={currentUser?.toJSON()} />

Oznacza to, że komponent <Header> renderuje dane użytkownika (jeśli są dostępne) w czasie wykonywania na serwerze. Jeśli w trakcie cyklu życia strony po jej początkowym załadowaniu nastąpią jakieś zmiany uwierzytelniania, obsłuży je element onAuthStateChanged.

Teraz nadszedł czas na wdrożenie nowej wersji i sprawdzenie jej.

  1. Utwórz zatwierdzanie z komunikatem „Pokaż stan logowania” i prześlij je do repozytorium GitHub.
  2. W konsoli Firebase otwórz stronę Hosting aplikacji i zaczekaj na zakończenie nowego wdrożenia.
  3. Sprawdź nowe zachowanie uwierzytelniania:
    1. W przeglądarce odśwież aplikację internetową. W nagłówku pojawi się Twoja nazwa wyświetlana.
    2. Wyloguj się i zaloguj ponownie. Strona jest aktualizowana w czasie rzeczywistym bez jej odświeżania. Możesz powtórzyć ten krok z innymi użytkownikami.
    3. Opcjonalnie: kliknij prawym przyciskiem myszy aplikację internetową, wybierz Wyświetl źródło strony i znajdź wyświetlaną nazwę. Występuje w surowym kodzie HTML zwróconym z serwera.

7. Wyświetlanie informacji o restauracji

Aplikacja internetowa zawiera przykładowe dane o restauracjach i recenzjach.

Dodawanie co najmniej 1 restauracji

Aby wstawić do lokalnej bazy danych Cloud Firestore dane testowej restauracji:

  1. W aplikacji internetowej kliknij 2cf67d488d8e6332.png > Dodaj przykładowe restauracje.
  2. W konsoli Firebase na stronie Baza danych Firestore kliknij restaurants. W kolekcji restauracji zobaczysz najwyższego poziomu dokumenty, z których każdy reprezentuje restaurację.
  3. Kliknij kilka dokumentów, aby poznać właściwości dokumentu restauracji.

Wyświetlanie listy restauracji

Twoja baza danych Cloud Firestore zawiera teraz restauracje, które aplikacja internetowa Next.js może wyświetlać.

Aby zdefiniować kod pobierania danych:

  1. W pliku src/app/page.js odszukaj komponent serwera <Home /> i sprawdź wywołanie funkcji getRestaurants, która pobiera listę restauracji w czasie wykonywania serwera. Funkcję getRestaurants wdrażasz w podanych niżej krokach.
  2. W pliku src/lib/firebase/firestore.js zastąp funkcje applyQueryFiltersgetRestaurants tym 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(),
		};
	});
}
  1. Utwórz zatwierdzanie z komunikatem „Czytaj listę restauracji z Firestore” i prześlij je do repozytorium GitHub.
  2. W konsoli Firebase otwórz stronę Hosting aplikacji i zaczekaj na zakończenie nowego wdrożenia.
  3. W aplikacji internetowej odśwież stronę. Zdjęcia restauracji są wyświetlane na stronie w postaci kafelków.

Sprawdź, czy informacje o restauracjach wczytują się w czasie działania serwera.

W ramach frameworku Next.js trudno określić, czy dane są wczytywane po stronie serwera, czy po stronie klienta.

Aby sprawdzić, czy wizytówki restauracji wczytują się w czasie działania serwera, wykonaj te czynności:

  1. W aplikacji internetowej otwórz narzędzia dla deweloperów i wyłącz JavaScript.

Wyłączanie JavaScriptu w Narzędziach deweloperskich

  1. Odśwież aplikację internetową. Informacje o restauracjach nadal się wczytują. Informacje o restauracji są zwracane w odpowiedzi serwera. Gdy JavaScript jest włączony, informacje o restauracji są hydratyzowane za pomocą kodu JavaScript po stronie klienta.
  2. W Narzędziach deweloperskich ponownie włącz JavaScript.

Nasłuchiwanie aktualizacji restauracji za pomocą odbiorników zrzutów Cloud Firestore

W poprzedniej sekcji pokazaliśmy, jak wczytać początkowy zestaw restauracji z pliku src/app/page.js. Plik src/app/page.js to komponent serwera, który jest renderowany na serwerze, w tym kod pobierania danych Firebase.

Plik src/components/RestaurantListings.jsx to komponent klienta, który można skonfigurować w celu nasycania znaczników renderowanych na serwerze.

Aby skonfigurować plik src/components/RestaurantListings.jsx do nasycania znaczników renderowanych na serwerze, wykonaj te czynności:

  1. W pliku src/components/RestaurantListings.jsx zwróć uwagę na ten kod, który jest już w nim zapisany:
useEffect(() => {
        const unsubscribe = getRestaurantsSnapshot(data => {
                setRestaurants(data);
        }, filters);

        return () => {
                unsubscribe();
        };
}, [filters]);

Ten kod wywołuje funkcję getRestaurantsSnapshot(), która jest podobna do funkcji getRestaurants() zaimplementowanej w poprzednim kroku. Funkcja zrzutu zapewnia jednak mechanizm wywołania zwrotnego, który jest wywoływany za każdym razem, gdy w zbiorze restauracji zostanie wprowadzona zmiana.

  1. W pliku src/lib/firebase/firestore.js zastą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);

	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;
}

Zmiany wprowadzone na stronie Baza danych Firestore są teraz odzwierciedlane w aplikacji internetowej w czasie rzeczywistym.

  1. Utwórz zatwierdzenie z komunikatem „Listen for realtime restaurant updates” i prześlij je do repozytorium GitHub.
  2. W konsoli Firebase otwórz stronę Hosting aplikacji i poczekaj na zakończenie nowego wdrożenia.
  3. W aplikacji internetowej kliknij 27ca5d1e8ed8adfe.png > Dodaj przykładowe restauracje. Jeśli funkcja migawki jest prawidłowo zaimplementowana, restauracje będą się wyświetlać w czasie rzeczywistym bez odświeżania strony.

8. Zapisywanie opinii przesłanych przez użytkowników z aplikacji internetowej

  1. W pliku src/lib/firebase/firestore.js zastą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 aktualne dane o liczbie ocen i średniej obliczonej ocenie.

  1. 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

Akcją serwera Next.js jest wygodny interfejs API umożliwiający dostęp do danych formularza, np. data.get("text"), aby uzyskać wartość tekstową z ładunku przesyłanego formularza.

Aby przetworzyć przesłany formularz opinii, użyj akcji serwera Next.js:

  1. W pliku src/components/ReviewDialog.jsx znajdź atrybut action w elemencie <form>.
<form action={handleReviewFormSubmission}>

Wartość atrybutu action odnosi się do funkcji, którą zaimplementujesz w następnym kroku.

  1. W pliku src/app/actions.js zastą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żyłeś obsługę przesyłania opinii, więc możesz teraz sprawdzić, czy opinie są prawidłowo wstawiane do Cloud Firestore.

Aby dodać opinię i sprawdzić, czy została ona dodana do Cloud Firestore:

  1. Utwórz zatwierdzanie z komunikatem „Zezwalaj użytkownikom na przesyłanie opinii o restauracjach” i prześlij je do repozytorium GitHub.
  2. W konsoli Firebase otwórz stronę Hosting aplikacji i zaczekaj na zakończenie nowego wdrożenia.
  3. Odśwież aplikację internetową i na stronie głównej wybierz restaurację.
  4. Na stronie restauracji kliknij 3e19beef78bb0d0e.png.
  5. Wybierz liczbę gwiazdek.
  6. Napisz opinię.
  7. Kliknij Prześlij. Opinia pojawi się u góry listy opinii.
  8. W Cloud Firestore wyszukaj w panelu Dodaj dokument dokument z restauracją, który chcesz ocenić, i go wybierz.
  9. W panelu Rozpocznij zbieranie wybierz oceny.
  10. W panelu Dodaj dokument odszukaj dokument, który chcesz sprawdzić, aby upewnić się, że został wstawiony zgodnie z oczekiwaniami.

Dokumenty w emulatorze Firestore

9. Zapisywanie plików przesłanych przez użytkowników z aplikacji internetowej

W tej sekcji dodasz funkcję, która umożliwi Ci zastąpienie po zalogowaniu się obrazu powiązanego z restauracją. Przesyłasz obraz do Firebase Storage i aktualizujesz adres URL obrazu w dokumencie Cloud Firestore, który reprezentuje restaurację.

Aby zapisać pliki przesłane przez użytkowników z aplikacji internetowej:

  1. W pliku src/components/Restaurant.jsx sprawdź kod, który jest wykonywany, gdy użytkownik prześle plik:
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 });
}

Nie musisz wprowadzać żadnych zmian, ale w następujących krokach musisz zaimplementować działanie funkcji updateRestaurantImage().

  1. W pliku src/lib/firebase/storage.js zastąp funkcje updateRestaurantImage()uploadImage() 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() została już wdrożona. Ta funkcja aktualizuje istniejący dokument restauracji w Cloud Firestore, dodając do niego zaktualizowany adres URL obrazu.

Sprawdzanie funkcji przesyłania obrazów

Aby sprawdzić, czy obrazy są przesyłane zgodnie z oczekiwaniami:

  1. Utwórz zatwierdzenie z wiadomością „Zezwalaj użytkownikom na zmianę zdjęcia każdej restauracji” i prześlij je do repozytorium GitHub.
  2. W konsoli Firebase otwórz stronę Hosting aplikacji i poczekaj na zakończenie nowego wdrożenia.
  3. W aplikacji internetowej sprawdź, czy jesteś zalogowany(-a), i wybierz restaurację.
  4. Kliknij 7067eb41fea41ff0.png 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.
  5. Otwórz Cloud Storage dla Firebase.
  6. Otwórz folder reprezentujący restaurację. Przesłany obraz znajduje się w folderze.

6cf3f9e2303c931c.png

10. Streszczanie opinii o restauracjach za pomocą generatywnej AI

W tej sekcji dodasz funkcję podsumowania opinii, aby użytkownik mógł szybko dowiedzieć się, co inni myślą o restauracji, bez konieczności czytania każdej opinii.

Przechowywanie klucza interfejsu Gemini API w usłudze Cloud Secret Manager

  1. Aby korzystać z interfejsu Gemini API, musisz mieć klucz interfejsu API. Utwórz klucz w Google AI Studio.
  2. Hostowanie aplikacji integruje się z Cloud Secret Manager, aby umożliwić bezpieczne przechowywanie wartości wrażliwych, takich jak klucze interfejsu API:
    1. Aby utworzyć nowy obiekt tajny, uruchom w terminalu to polecenie:
    firebase apphosting:secrets:set gemini-api-key
    
    1. Gdy pojawi się prośba o wartość tajną, skopiuj i wklej klucz interfejsu Gemini API z Google AI Studio.
    2. Gdy pojawi się pytanie, czy nowy klucz powinien zostać dodany do apphosting.yaml, wpisz Y, aby zaakceptować.

Klucz interfejsu Gemini API jest teraz bezpiecznie przechowywany w usłudze Cloud Secret Manager i jest dostępny dla backendu usługi hostowania aplikacji.

Wdrażanie komponentu podsumowania opinii

  1. W pliku src/components/Reviews/ReviewSummary.jsx zastąp funkcję GeminiSummary tym kodem:
    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>;
        }
    }
    
  2. Utwórz zatwierdzanie z komunikatem „Użyj AI do podsumowywania opinii” i prześlij je do repozytorium GitHub.
  3. W konsoli Firebase otwórz stronę Hosting aplikacji i poczekaj na zakończenie nowego wdrożenia.
  4. Otwórz stronę restauracji. U góry strony powinno się wyświetlać podsumowanie wszystkich opinii na stronie w jednym zdaniu.
  5. Dodaj nową opinię i odśwież stronę. Powinna się zmienić treść podsumowania.

11. Podsumowanie

Gratulacje! Dowiedz się, jak za pomocą Firebase dodać funkcje do aplikacji Next.js. W szczególności wykorzystano te elementy:

Więcej informacji