Zintegruj Firebase z aplikacją Next.js

1. Zanim zaczniesz

Z tego ćwiczenia w Codelabs dowiesz się, jak zintegrować Firebase z aplikacją internetową Next.js o nazwie „Przyjazne jedzenie” – witryną z opiniami o restauracjach.

Aplikacja internetowa Friends Eats

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

  • Automatyczne kompilowanie i wdrażanie: to ćwiczenie w Codelabs korzysta z Firebase App Hosting do automatycznego kompilowania i wdrażania kodu Next.js za każdym razem, gdy wypchniesz go do skonfigurowanej gałęzi.
  • Logowanie się i wylogowywanie: ukończona aplikacja internetowa umożliwia zalogowanie się przez Google i wylogowanie się. Zalogowanie się użytkownika i jego trwałość odbywa się całkowicie przez Uwierzytelnianie Firebase.
  • Obrazy: ukończona aplikacja internetowa umożliwia zalogowanym użytkownikom przesyłanie zdjęć restauracji. Zasoby graficzne są przechowywane w Cloud Storage dla Firebase. Pakiet SDK Firebase JavaScript udostępnia publiczny adres URL do przesyłanych obrazów. Ten publiczny adres URL jest następnie zapisywany w odpowiednim dokumencie restauracji w Cloud Firestore.
  • Opinie: wypełniona aplikacja internetowa umożliwia zalogowanym użytkownikom publikowanie opinii o restauracjach w postaci gwiazdek i wiadomości tekstowych. Informacje o opiniach są przechowywane w Cloud Firestore.
  • Filtry: ukończona aplikacja internetowa umożliwia zalogowanym użytkownikom filtrowanie listy restauracji według 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 utrwalać obrazy w Cloud Storage dla Firebase.
  • Jak odczytywać i zapisywać dane w bazie danych Cloud Firestore.
  • Jak korzystać z funkcji logowania przez Google za pomocą pakietu SDK Firebase JavaScript.

Czego potrzebujesz

  • Git
  • Najnowsza stabilna wersja Node.js
  • wybraną przeglądarkę, na przykład Google Chrome;
  • Środowisko programistyczne z edytorem kodu i terminalem
  • Konto Google do tworzenia projektu Firebase i zarządzania nim
  • możliwość uaktualnienia projektu Firebase do abonamentu Blaze,

2. Skonfiguruj środowisko programistyczne i repozytorium GitHub

To ćwiczenie w Codelabs wykorzystuje startową bazę kodu aplikacji i opiera się na interfejsie wiersza poleceń Firebase.

Tworzenie repozytorium GitHub

Źródło ćwiczeń w Codelabs znajdziesz na https://github.com/firebase/friendeats-web. Repozytorium zawiera przykładowe projekty na wiele platform. Jednak w tym ćwiczeniu w Codelabs używany jest tylko katalog 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. W terminalu utwórz nowy folder na komputerze i przejdź do nowego katalogu:
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. Użyj pakietu npm giget, aby pobrać tylko folder nextjs-start:
    npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
    
  3. Śledzenie zmian lokalnie przez 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 ją nazwać dowolnie.
    1. Serwis GitHub wyświetli nowy adres URL repozytorium, który wygląda podobnie do tego: https://github.com//.git lub git@github.com:/.git. Skopiuj ten adres URL.
  5. Prześlij lokalne zmiany do nowego repozytorium GitHub. Uruchom to polecenie, zastępując adres URL repozytorium zmienną .
    git remote add origin <your-repository-url>
    
    git push -u origin main
    
  6. Kod startowy powinien pojawić się w Twoim repozytorium GitHub.

Instalowanie lub aktualizowanie interfejsu wiersza poleceń Firebase

Uruchom poniższe polecenie, aby sprawdzić, czy masz zainstalowany interfejs wiersza poleceń Firebase i czy jest to wersja 13.9.0 lub nowsza:

firebase --version

Jeśli widzisz niższą wersję lub nie masz zainstalowanego interfejsu wiersza poleceń Firebase, uruchom polecenie instalacji:

npm install -g firebase-tools@latest

Jeśli nie możesz zainstalować interfejsu wiersza poleceń Firebase z powodu błędów uprawnień, zapoznaj się z dokumentacją npm lub użyj innej opcji instalacji.

Zaloguj się w Firebase

  1. Uruchom to polecenie, aby zalogować się w interfejsie wiersza poleceń Firebase:
    firebase login
    
  2. W zależności od tego, czy chcesz, aby Firebase gromadził dane, wpisz Y czy N.
  3. W przeglądarce wybierz swoje konto Google, a następnie kliknij Zezwól.

3. Skonfiguruj projekt 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

  1. W konsoli Firebase kliknij Dodaj projekt.
  2. W polu tekstowym Wpisz nazwę projektu wpisz FriendlyEats Codelab (lub inną nazwę projektu), a potem kliknij Dalej.
  3. W oknie Potwierdź abonament Firebase potwierdź, że abonament to Blaze, a następnie kliknij Potwierdź abonament.
  4. Ćwiczenia z programowania nie wymagają Google Analytics, więc wyłącz opcję Włącz Google Analytics dla tego projektu.
  5. Kliknij Utwórz projekt.
  6. Poczekaj na udostępnienie projektu, a potem kliknij Dalej.
  7. W projekcie Firebase otwórz Ustawienia projektu. Zapisz identyfikator projektu, ponieważ będzie on potrzebny później. Ten unikalny identyfikator służy do identyfikowania projektu (np. w interfejsie wiersza poleceń Firebase).

Uaktualnij abonament Firebase

Aby korzystać z App Hosting, Twój projekt Firebase musi być objęty abonamentem Blaze, co oznacza, że jest powiązany z kontem rozliczeniowym Cloud.

  • Do konta rozliczeniowego Cloud wymagana jest forma płatności, np. karta kredytowa.
  • Jeśli dopiero zaczynasz korzystać z Firebase i Google Cloud, sprawdź, czy kwalifikujesz się do otrzymania środków w wysokości 300 USD i bezpłatnego okresu próbnego konta rozliczeniowego Cloud.

Aby przenieść projekt na abonament Blaze, wykonaj te czynności:

  1. W konsoli Firebase wybierz Przejdź na większy pakiet.
  2. W oknie wybierz abonament Blaze i postępuj zgodnie z instrukcjami wyświetlanymi na ekranie, aby powiązać projekt z kontem rozliczeniowym Cloud.
    Jeśli konieczne jest utworzenie konta rozliczeniowego Cloud, może być konieczne powrót do procesu uaktualniania w konsoli Firebase.

Dodawanie aplikacji internetowej do projektu Firebase

  1. W projekcie Firebase otwórz Przegląd projektu i kliknij e41f2efdd9539c31.png Sieć.

    Jeśli masz już w projekcie zarejestrowane aplikacje, kliknij Dodaj aplikację, żeby wyświetlić ikonę internetu.
  2. W polu tekstowym Pseudonim aplikacji wpisz zapadający w pamięć pseudonim aplikacji, np. My Next.js app.
  3. Pozostaw pole Skonfiguruj też Hosting Firebase dla tej aplikacji niezaznaczone.
  4. Kliknij Zarejestruj aplikację > Dalej > Dalej > Przejdź do konsoli.

Konfigurowanie usług Firebase w konsoli Firebase

Skonfiguruj uwierzytelnianie

  1. W konsoli Firebase otwórz Uwierzytelnianie.
  2. Kliknij Rozpocznij.
  3. W kolumnie Dodatkowi dostawcy kliknij Google > Włącz.
  4. W polu tekstowym Publiczna nazwa projektu wpisz łatwą do zapamiętania nazwę, na przykład My Next.js app.
  5. Z listy Adres e-mail pomocy dla projektu wybierz swój adres e-mail.
  6. Kliknij Zapisz.

Konfigurowanie Cloud Firestore

  1. W konsoli Firebase otwórz Firestore.
  2. Kliknij Utwórz bazę danych > Dalej > Uruchom w trybie testowym > Dalej.
    W dalszej części tego ćwiczenia z programowania dodasz reguły zabezpieczeń, aby zabezpieczyć swoje dane. Nie udostępniaj ani nie udostępniaj aplikacji publicznie bez dodawania reguł zabezpieczeń do bazy danych.
  3. Użyj lokalizacji domyślnej lub wybierz lokalizację.
    W przypadku prawdziwej aplikacji wybierz lokalizację znajdującą się w pobliżu użytkowników. Pamiętaj, że tej lokalizacji nie można później zmienić. Będzie to również automatycznie lokalizacja domyślnego zasobnika Cloud Storage (następny krok).
  4. Kliknij Gotowe.

Konfigurowanie Cloud Storage dla Firebase

  1. W konsoli Firebase otwórz Miejsce na dane.
  2. Kliknij Rozpocznij > Uruchom w trybie testowym > Dalej.
    W dalszej części tego ćwiczenia z programowania dodasz reguły zabezpieczeń, aby zabezpieczyć swoje dane. Nie rozpowszechniaj ani nie udostępniaj aplikacji publicznie bez dodawania reguł zabezpieczeń do zasobnika na dane.
  3. Lokalizacja zasobnika powinna już być wybrana (ze względu na konfigurowanie Firestore w poprzednim kroku).
  4. Kliknij Gotowe.

4. Przejrzyj bazę kodu startową

W tej sekcji zapoznasz się z kilkoma obszarami bazy kodu startowego aplikacji, do których dodasz funkcje w ramach tego ćwiczenia.

Struktura folderów i plików

Poniższa tabela zawiera omówienie struktury folderów i plików aplikacji:

Foldery i pliki

Opis

src/components

Komponenty reakcji na filtry, nagłówki, szczegóły restauracji i opinie

src/lib

Funkcje użytkowe, które nie są koniecznie powiązane z React lub Next.js

src/lib/firebase

Kod i konfiguracja Firebase

public

zasoby statyczne w aplikacji internetowej, takie jak ikony.

src/app

Routing przy użyciu routera aplikacji Next.js

src/app/restaurant

Moduł obsługi trasy interfejsu API

package.json i package-lock.json

Zależności projektu z npm

next.config.js

Konfiguracja Next.js (działania serwera są włączone)

jsconfig.json

Konfiguracja usługi języka JavaScript

Komponenty serwera i klienta

Jest to aplikacja internetowa Next.js, która używa routera aplikacji. Renderowanie 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 wskazywany przez dyrektywę "use client" na początku pliku.

Importuj instrukcje

Możesz zauważyć instrukcje importu podobne do tych:

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

Aplikacja używa symbolu @, aby uniknąć zbędnych ścieżek importu względnego. 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. Zamiast importować funkcje Firebase bezpośrednio, poszczególne komponenty React zaimportują opakowane funkcje z katalogu src/lib/firebase.

Pozorowane dane

Dane o restauracjach i opiniach na ten temat znajdują się w pliku src/lib/randomData.js. Dane z tego pliku są umieszczane w kodzie w pliku src/lib/fakeRestaurants.js.

5. Tworzenie backendu App Hosting

W tej sekcji skonfigurujesz backend App Hosting do obserwowania gałęzi w repozytorium Git.

Na końcu tej sekcji będziesz mieć połączony backend App Hosting z Twoim repozytorium w GitHubie, który będzie automatycznie ponownie skompilować i wdrożyć nową wersję Twojej aplikacji za każdym razem, gdy wypchniesz nowe zatwierdzenie do gałęzi main.

Wdrażanie reguł zabezpieczeń

Ten kod ma już zestawy reguł zabezpieczeń dla Firestore oraz Cloud Storage dla Firebase. Po wdrożeniu reguł zabezpieczeń dane w bazie danych i zasobniku są lepiej chronione przed niewłaściwym wykorzystaniem.

  1. W terminalu skonfiguruj interfejs wiersza poleceń tak, aby używał utworzonego wcześniej projektu Firebase:
    firebase use --add
    
    Gdy pojawi się prośba o podanie aliasu, 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 zapytamy: "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. W panelu Konfiguracja i konfiguracja pakietu SDK kliknij „Dodaj aplikację”. i kliknij ikonę nawiasów kodu , aby zarejestrować nową aplikację internetową.
  3. Na końcu procesu tworzenia aplikacji internetowej skopiuj zmienną firebaseConfig i skopiuj jej właściwości wraz z wartościami.
  4. Otwórz plik apphosting.yaml w edytorze kodu i uzupełnij wartości zmiennych środowiskowych wartościami konfiguracyjnymi z konsoli Firebase.
  5. Zastąp obecne właściwości w pliku tymi, które zostały skopiowane.
  6. Zapisz plik.

Utworzenie backendu

  1. W konsoli Firebase otwórz stronę App Hosting (Hosting aplikacji):

Stan zero konsoli App Hosting z napisem „Rozpocznij” przycisk

  1. Kliknij „Rozpocznij”. aby rozpocząć proces tworzenia backendu. Skonfiguruj backend w ten sposób:
  2. Postępuj zgodnie z instrukcjami w pierwszym kroku, aby połączyć utworzone wcześniej repozytorium GitHub.
  3. Skonfiguruj ustawienia wdrożenia:
    1. Zachowaj katalog główny jako /
    2. Ustaw aktywną gałąź na main
    3. Włącz automatyczne wdrażanie
  4. Nazwij swój backend friendlyeats-codelab.
  5. W sekcji „Utwórz lub powiąż aplikację internetową Firebase” w sekcji „Wybierz istniejącą aplikację internetową Firebase” wybierz skonfigurowaną wcześniej aplikację internetową. listę rozwijaną.
  6. Kliknij „Zakończ i wdróż”. Po chwili otworzy się nowa strona, na której możesz sprawdzić stan nowego backendu App Hostingu.
  7. Po zakończeniu wdrażania kliknij bezpłatną domenę w sekcji „domains” (domeny). Rozpoczęcie pracy może potrwać kilka minut z powodu rozpowszechnienia DNS.

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

6. Dodawanie uwierzytelniania do aplikacji internetowej

W tej sekcji dodasz uwierzytelnianie do aplikacji internetowej, aby umożliwić Ci logowanie się do niej.

Implementowanie funkcji logowania i wylogowywania

  1. W pliku src/lib/firebase/auth.js zastąp funkcje onAuthStateChanged, signInWithGoogle i signOut 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 wykorzystuje następujące interfejsy API Firebase:

Interfejs Firebase API

Opis

GoogleAuthProvider

Tworzy instancję dostawcy uwierzytelniania Google.

signInWithPopup

Uruchamia proces uwierzytelniania w oknie.

auth.signOut

Wylogowuje użytkownika.

W pliku src/components/Header.jsx kod wywołuje już funkcje signInWithGoogle i signOut.

  1. Utwórz zatwierdzenie z komunikatem „Dodawanie uwierzytelniania Google” i wypchnij go do repozytorium GitHub. 1. Otwórz stronę App Hosting (Hosting aplikacji) w konsoli Firebase 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 ma pewności, czy logowanie się udało.

Wysyłaj stan uwierzytelniania na serwer

Aby przekazywać stan uwierzytelniania do serwera, używamy skryptu service worker. Zastąp funkcje fetchWithFirebaseHeaders i getAuthIdToken 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);
}

Odczytywanie stanu uwierzytelniania na serwerze

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

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

Subskrybuj zmiany dotyczące uwierzytelniania

Aby zasubskrybować zmiany dotyczące uwierzytelniania, wykonaj te czynności:

  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 korzysta z haczyka state React, aby zaktualizować użytkownika, gdy funkcja onAuthStateChanged określi, że nastąpiła zmiana stanu uwierzytelniania.

Zweryfikuj zmiany

Układ główny w pliku src/app/layout.js renderuje nagłówek i przekazuje go do użytkownika, 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 działania serwera. Jeśli w cyklu życia strony po jej pierwszym wczytaniu pojawią się jakieś aktualizacje uwierzytelniania, moduł obsługi onAuthStateChanged je zajmie.

Nadszedł czas, aby wdrożyć nową kompilację i sprawdzić, co udało Ci się stworzyć.

  1. Utwórz zatwierdzenie z komunikatem „Pokaż stan logowania” i wypchnij go do repozytorium GitHub.
  2. Otwórz stronę App Hosting (Hosting aplikacji) w konsoli Firebase i poczekaj na zakończenie nowego wdrożenia.
  3. Sprawdź nowy sposób uwierzytelniania:
    1. W przeglądarce odśwież aplikację internetową. Twoja wyświetlana nazwa pojawi się w nagłówku.
    2. Wyloguj się i zaloguj ponownie. Strona jest aktualizowana w czasie rzeczywistym bez jej odświeżania. Możesz powtórzyć ten krok dla różnych użytkowników.
    3. Opcjonalnie: kliknij aplikację internetową prawym przyciskiem myszy, wybierz Wyświetl źródło strony i wyszukaj wyświetlaną nazwę. Pojawia się on w nieprzetworzonym źródle HTML zwróconym z serwera.

7. Wyświetlanie informacji o restauracjach

Aplikacja internetowa zawiera przykładowe dane restauracji i opinii.

Dodaj co najmniej jedną restaurację

Aby wstawić dane przykładowej restauracji do lokalnej bazy danych Cloud Firestore, wykonaj te czynności:

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

Wyświetlanie listy restauracji

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

Aby zdefiniować kod pobierania danych, wykonaj te czynności:

  1. W pliku src/app/page.js znajdź komponent serwera <Home /> i zapoznaj się z wywołaniem funkcji getRestaurants, która pobiera listę restauracji w czasie działania serwera. Funkcję getRestaurants implementujesz w poniższych krokach.
  2. W pliku src/lib/firebase/firestore.js zastąp funkcje applyQueryFilters i getRestaurants 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 zatwierdzenie z komunikatem „Odczytywanie listy restauracji z Firestore” i wypchnij go do repozytorium GitHub.
  2. Otwórz stronę App Hosting (Hosting aplikacji) w konsoli Firebase i poczekaj na zakończenie nowego wdrożenia.
  3. W aplikacji internetowej odśwież stronę. Obrazy z restauracji pojawiają się na stronie jako kafelki.

Sprawdzanie, czy informacje o restauracjach wczytują się podczas działania serwera

W przypadku platformy Next.js nie jest oczywiste, czy dane są wczytywane w czasie działania serwera czy po stronie klienta.

Aby sprawdzić, czy informacje o restauracjach wczytują się w czasie działania serwera, wykonaj te czynności:

  1. W aplikacji internetowej otwórz Narzędzia deweloperskie i wyłącz JavaScript.

Wyłącz JavaScipt w Narzędziach deweloperskich

  1. Odśwież aplikację internetową. Wizytówki restauracji nadal się wczytują. Informacje o restauracji są zwracane w odpowiedzi serwera. Gdy włączona jest obsługa JavaScriptu, informacje o restauracji są hydratowane za pomocą kodu JavaScript po stronie klienta.
  2. W Narzędziach deweloperskich ponownie włącz JavaScript.

Wykrywaj aktualizacje restauracji za pomocą detektorów zrzutów w Cloud Firestore

W poprzedniej sekcji pokazaliśmy, jak początkowy zestaw restauracji wczytywał się z pliku src/app/page.js. Plik src/app/page.js jest komponentem serwera i jest renderowany na serwerze, w tym kod Firebase pobierający dane.

Plik src/components/RestaurantListings.jsx jest komponentem klienckim i można go skonfigurować tak, by hydratował znaczniki renderowane przez serwer.

Aby skonfigurować plik src/components/RestaurantListings.jsx do hydratacji znaczników renderowanych przez serwer, wykonaj te czynności:

  1. W pliku src/components/RestaurantListings.jsx zwróć uwagę na poniższy kod, który został już dla Ciebie napisany:
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. Ta funkcja zrzutu udostępnia jednak mechanizm wywołania zwrotnego, dzięki czemu wywołanie zwrotne jest wywoływane po każdej zmianie kolekcji restauracji.

  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 „Nasłuchuj aktualizacji restauracji w czasie rzeczywistym” i wypchnij go do repozytorium GitHub.
  2. Otwórz stronę App Hosting (Hosting aplikacji) w konsoli Firebase i poczekaj na zakończenie nowego wdrożenia.
  3. W aplikacji internetowej wybierz 27ca5d1e8ed8adfe.png > Dodaj przykładowe restauracje. Jeśli funkcja zrzutu jest prawidłowo zaimplementowana, restauracje wyświetlają się 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ż aktualny dokument Firestore, który reprezentuje restaurację, o zaktualizowane wartości dotyczące liczby ocen i średniej obliczonej oceny.

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

Wdróż działanie serwera Next.js

Akcja serwera Next.js zapewnia wygodny interfejs API umożliwiający dostęp do danych formularza, np. data.get("text"), który pozwala pobierać wartość tekstową z ładunku przesłanego formularza.

Aby do przetworzenia formularza weryfikacji użyć akcji serwera Next.js, wykonaj te czynności:

  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

Udało Ci się wdrożyć obsługę przesyłania opinii, więc możesz teraz sprawdzić, czy recenzje są prawidłowo wstawiane do Cloud Firestore.

Aby dodać opinię i sprawdzić, czy jest ona wstawiona do Cloud Firestore, wykonaj te czynności:

  1. Utwórz zatwierdzenie z komunikatem „Zezwalaj użytkownikom na przesyłanie opinii o restauracjach” i wypchnij go do repozytorium GitHub.
  2. Otwórz stronę App Hosting (Hosting aplikacji) w konsoli Firebase i poczekaj 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. Twoja opinia wyświetli się na początku listy opinii.
  8. W Cloud Firestore wyszukaj w panelu Dodaj dokument dokument restauracji, który został przez Ciebie sprawdzony, i wybierz go.
  9. W panelu Rozpocznij kolekcję wybierz oceny.
  10. W panelu Dodaj dokument znajdź dokument do sprawdzenia, aby sprawdzić, czy 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 możesz dodać funkcje, które po zalogowaniu umożliwiają zastępowanie 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 za pomocą aplikacji internetowej, wykonaj te czynności:

  1. Obserwuj w pliku src/components/Restaurant.jsx kod, który uruchamia się, 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);
        setRestaurant({ ...restaurant, photo: imageURL });
}

Nie musisz wprowadzać żadnych zmian, ale w kolejnych krokach wdrożysz działanie funkcji updateRestaurantImage().

  1. W pliku src/lib/firebase/storage.js zastąp funkcje updateRestaurantImage() i 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() jest już zaimplementowana. Ta funkcja aktualizuje istniejący dokument restauracji w Cloud Firestore o zaktualizowany adres URL obrazu.

Sprawdzanie funkcji przesyłania obrazów

Aby sprawdzić, czy zdjęcie jest przesyłane zgodnie z oczekiwaniami, wykonaj te czynności:

  1. Utwórz zatwierdzenie z komunikatem „Zezwalaj użytkownikom na zmienianie poszczególnych restauracji” zdjęcie” i wypchnij go do repozytorium GitHub.
  2. Otwórz stronę App Hosting (Hosting aplikacji) w konsoli Firebase i poczekaj na zakończenie nowego wdrożenia.
  3. W aplikacji internetowej zaloguj się i wybierz restaurację.
  4. Kliknij 7067eb41fea41ff0.png i prześlij obraz z systemu plików. Obraz opuszcza środowisko lokalne i zostaje przesłany do Cloud Storage. Obraz pojawi się od razu po jego przesłaniu.
  5. Otwórz Cloud Storage dla Firebase.
  6. Przejdź do folderu zawierającego restaurację. Przesłany obraz znajduje się w folderze.

6cf3f9e2303c931c.png

10. Podsumowuj opinie o restauracjach za pomocą generatywnej AI

W tej sekcji dodasz funkcję podsumowania opinii, aby użytkownicy mogli szybko zrozumieć, co wszyscy myślą o restauracji, bez konieczności czytania każdej opinii.

Przechowywanie klucza interfejsu Gemini API w Cloud Secret Manager

  1. Aby korzystać z Gemini API, potrzebujesz klucza interfejsu API. Utwórz klucz w Google AI Studio
  2. App Hosting integruje się z usługą Cloud Secret Manager, co umożliwia bezpieczne przechowywanie poufnych wartości, takich jak klucze interfejsu API:
    1. W terminalu uruchom polecenie, aby utworzyć nowy obiekt tajny:
    firebase apphosting:secrets:set gemini-api-key
    
    1. Gdy pojawi się prośba o podanie wartości obiektu tajnego, skopiuj i wklej swój klucz interfejsu Gemini API z Google AI Studio.
    2. Gdy pojawi się pytanie, czy nowy obiekt tajny ma zostać dodany do apphosting.yaml, wpisz Y, aby zaakceptować.

Twój klucz interfejsu Gemini API jest teraz bezpiecznie przechowywany w usłudze Cloud Secret Manager i jest dostępny dla backendu App Hosting.

Implementowanie komponentu podsumowania opinii

  1. W 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 zatwierdzenie z komunikatem „Używaj AI do podsumowania opinii” i wypchnij go do repozytorium GitHub.
  3. Otwórz stronę App Hosting (Hosting aplikacji) w konsoli Firebase i poczekaj na zakończenie nowego wdrożenia.
  4. Otwórz stronę restauracji. U góry powinno pojawić się jednozdaniowe podsumowanie wszystkich opinii na stronie.
  5. Dodaj nową opinię i odśwież stronę. Podsumowanie zmian powinno być widoczne.

11. Podsumowanie

Gratulacje! Wiesz już, jak używać Firebase, aby dodawać funkcje do aplikacji Next.js. Konkretnie chodzi o:

Więcej informacji