Zintegruj Firebase z aplikacją Next.js

1. Zanim zaczniesz

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

Aplikacja internetowa przyjazna restauracja

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

  • Automatyczne kompilowanie i wdrażanie: w tym ćwiczeniu w programie korzystamy z platformy Firebase App Hosting, aby automatycznie kompilować i wdrażać kod Next.js za każdym razem, gdy wypchniesz do skonfigurowanej gałęzi.
  • Logowanie się i wylogowywanie: ukończona aplikacja internetowa umożliwia logowanie się przez Google i wylogowywanie się. Logowaniem użytkownika i trwałością działania użytkownika zarządza się w całości przez Uwierzytelnianie Firebase.
  • Obrazy: gotowa aplikacja internetowa umożliwia zalogowanym użytkownikom przesyłanie zdjęć restauracji. Zasoby graficzne są przechowywane w Cloud Storage dla Firebase. Pakiet Firebase JavaScript SDK udostępnia publiczny adres URL przesył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 są wyróżnione 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 według kategorii, lokalizacji i ceny. Możesz też dostosować używaną metodę sortowania. Dostęp do danych uzyskuje się 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 zachowywać obrazy w Cloud Storage dla Firebase.
  • Jak odczytywać i zapisywać dane w bazie danych Cloud Firestore.
  • Jak korzystać z funkcji logowania się przez Google za pomocą pakietu SDK Firebase JavaScript.

Czego potrzebujesz

  • Git
  • ostatnia stabilna wersja Node.js.
  • wybraną przeglądarkę, np. Google Chrome;
  • Środowisko programistyczne z edytorem kodu i terminalem
  • Konto Google używane do tworzenia projektu Firebase i zarządzania nim
  • możliwość przeniesienia projektu Firebase na abonament Blaze,

2. Konfigurowanie środowiska programistycznego i repozytorium GitHub

To ćwiczenie w Codelabs zapewnia początkową bazę kodu aplikacji i korzysta z interfejsu wiersza poleceń Firebase.

Tworzenie repozytorium GitHub

Kod źródłowy ćwiczeń z programowania znajdziesz na stronie https://github.com/firebase/friendeats-web. Repozytorium zawiera przykładowe projekty na wielu platformach. W tym ćwiczeniu w programowaniu wykorzystywany jest jednak 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. Za pomocą terminala utwórz na komputerze nowy folder i przejdź do nowego katalogu:
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. Użyj pakietu npm giget do pobrania tylko folderu nextjs-start:
    npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
    
  3. Śledź zmiany lokalnie za pomocą Gita:
    git init
    
    git commit -a -m "codelab starting point"
    
    git branch -M main
    
  4. Utwórz nowe repozytorium GitHub: https://github.com/new. Nadaj mu dowolną nazwę.
    1. GitHub udostępni Ci nowy adres URL repozytorium, który wygląda tak: https://github.com//.git lub git@github.com:/.git. Skopiuj ten adres URL.
  5. Przekaż zmiany lokalne do nowego repozytorium GitHub. Uruchom poniższe polecenie, zastępując URL repozytorium zmienną .
    git remote add origin <your-repository-url>
    
    git push -u origin main
    
  6. W Twoim repozytorium GitHub powinien być widoczny kod startowy.

Instalowanie lub aktualizowanie interfejsu wiersza poleceń Firebase

Uruchom to 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 starszą wersję lub nie masz zainstalowanego interfejsu 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 użyj innej opcji instalacji.

Zaloguj się w Firebase

  1. Aby zalogować się w interfejsie wiersza poleceń Firebase, uruchom to polecenie:
    firebase login
    
  2. W zależności od tego, czy chcesz zbierać dane przez Firebase, wpisz Y czy N.
  3. W przeglądarce wybierz swoje konto Google i 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ą.

Utwórz projekt Firebase

  1. W konsoli Firebase kliknij Dodaj projekt.
  2. W polu tekstowym Wpisz nazwę projektu wpisz FriendlyEats Codelab (lub dowolną nazwę projektu) i kliknij Dalej.
  3. W oknie Potwierdź abonament Firebase sprawdź, czy abonament to Blaze, a następnie kliknij Potwierdź plan.
  4. Aby korzystać z tych ćwiczeń z programowania, nie potrzebujesz Google Analytics, więc wyłącz opcję Włącz Google Analytics w tym projekcie.
  5. Kliknij Create project (Utwórz projekt).
  6. Poczekaj na udostępnienie projektu, a potem kliknij Continue (Dalej).
  7. W projekcie Firebase otwórz Ustawienia projektu. Zapisz identyfikator projektu, bo będzie Ci potrzebny później. Ten unikalny identyfikator określa sposób identyfikacji Twojego projektu (na przykład w interfejsie wiersza poleceń Firebase).

Uaktualnij abonament Firebase

Aby korzystać z hostingu aplikacji, projekt Firebase musi być objęty abonamentem Blaze, co oznacza, że jest powiązany z kontem rozliczeniowym Cloud.

  • Konto rozliczeniowe Cloud wymaga formy płatności, takiej jak karta kredytowa.
  • Jeśli jesteś nowym użytkownikiem Firebase i Google Cloud, sprawdź, czy kwalifikujesz się do otrzymania środków w wysokości 300 USD i bezpłatnego próbnego konta rozliczeniowego Cloud.

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

  1. W konsoli Firebase wybierz przejdź na wyższą wersję abonamentu.
  2. W oknie wybierz abonament Blaze i postępuj zgodnie z instrukcjami wyświetlanymi na ekranie, aby powiązać swój projekt z kontem rozliczeniowym Cloud.
    Jeśli musisz utworzyć konto rozliczeniowe Cloud, być może trzeba będzie wrócić do procesu uaktualniania w konsoli Firebase.

Dodaj aplikację internetową do projektu Firebase

  1. W projekcie Firebase otwórz Omówienie projektu i kliknij e41f2efdd9539c31.png Sieć.

    Jeśli w Twoim projekcie są już zarejestrowane aplikacje, kliknij Dodaj aplikację, aby zobaczyć ikonę aplikacji Internet.
  2. W polu tekstowym Pseudonim aplikacji wpisz łatwą do zapamiętania nazwę, np. My Next.js app.
  3. Nie zaznaczaj pola wyboru Skonfiguruj również Hosting Firebase dla tej aplikacji.
  4. Kliknij Zarejestruj aplikację > Dalej > Dalej > Przejdź do konsoli.

Skonfiguruj usługi Firebase w konsoli Firebase

Skonfiguruj uwierzytelnianie

  1. W konsoli Firebase otwórz Authentication (Uwierzytelnianie).
  2. Kliknij Rozpocznij.
  3. W kolumnie Dostawcy dodatkowi kliknij Google > Włącz.
  4. W polu tekstowym Nazwa projektu widoczna publicznie wpisz łatwą do zapamiętania nazwę, na przykład My Next.js app.
  5. Z listy Adres e-mail pomocy technicznej związanej z projektem 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 > Rozpocznij w trybie testowym > Dalej.
    Później w tym ćwiczeniu w Codelabs dodasz reguły zabezpieczeń, aby zabezpieczyć dane. Nie rozpowszechniaj ani nie udostępniaj aplikacji publicznie bez dodawania reguł zabezpieczeń do bazy danych.
  3. Użyj domyślnej lokalizacji lub wybierz inną.
    W przypadku prawdziwej aplikacji powinno być miejsce w pobliżu użytkowników. Pamiętaj, że tej lokalizacji nie można później zmienić. Stanie się ona też automatycznie lokalizacją 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 > Rozpocznij 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 skonfigurowanie Firestore w poprzednim kroku).
  4. Kliknij Gotowe.

4. Sprawdzanie bazy kodu startowego

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

Struktura folderów i plików

Tabela poniżej zawiera omówienie struktury folderów i plików aplikacji:

Foldery i pliki

Opis

src/components

Reaguj na komponenty filtrów, nagłówków, informacji o restauracjach i opinii

src/lib

Funkcje narzędziowe, które nie są powiązane z React ani z Next.js

src/lib/firebase

Kod związany z Firebase i konfiguracja Firebase

public

zasoby statyczne w aplikacji internetowej, np. ikony;

src/app

Routing za pomocą routera aplikacji Next.js

src/app/restaurant

Moduł obsługi tras interfejsu API

package.jsonpackage-lock.json

Zależności projektu z npm

next.config.js

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

jsconfig.json

Konfiguracja usługi językowej JavaScript

Komponenty serwera i klienta

Jest to aplikacja internetowa Next.js, która korzysta z funkcji App Router. W aplikacji używane jest renderowanie serwera. Na przykład plik src/app/page.js to komponent serwera odpowiedzialny za stronę główną. Plik src/components/RestaurantListings.jsx jest komponentem klienckim oznaczonym na początku dyrektywą "use client".

Importuj wyciągi

Możesz zauważyć takie instrukcje importu:

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

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

Interfejsy API związane z Firebase

Cały kod interfejsu Firebase API zostanie zawarty w katalogu src/lib/firebase. Poszczególne komponenty React importują opakowane funkcje z katalogu src/lib/firebase, zamiast bezpośrednio importować funkcje Firebase.

Pozorowanie danych

Plik src/lib/randomData.js zawiera fałszywe dane o restauracji i opiniach. Dane z tego pliku są zbierane w kodzie w pliku src/lib/fakeRestaurants.js.

5. Tworzenie backendu usługi App Hosting

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

Pod koniec tej sekcji połączysz backend Hostingu aplikacji z repozytorium w GitHubie, który będzie automatycznie ponownie skompilować i wdrożyć nową wersję 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 i 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 utworzenie 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 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.

Dodaj konfigurację Firebase do kodu aplikacji internetowej

  1. W konsoli Firebase otwórz Ustawienia projektu.
  2. W panelu Konfiguracja i konfiguracja pakietu SDK kliknij „Dodaj aplikację”, a następnie 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 oraz wartości.
  4. Otwórz plik apphosting.yaml w edytorze kodu i wpisz wartości zmiennych środowiskowych wartościami konfiguracyjnymi z konsoli Firebase.
  5. W pliku zastąp obecne właściwości tymi, które zostały skopiowane.
  6. Zapisz plik.

Utworzenie backendu

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

mieć zerowy stan konsoli Hostingu aplikacji z przyciskiem „Rozpocznij”;

  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 wdrażania:
    1. Zachowaj katalog główny jako /
    2. Ustaw aktywną gałąź na main
    3. Włącz automatyczne wdrażanie
  4. Nazwij backend friendlyeats-codelab.
  5. W sekcji „Utwórz lub powiąż aplikację internetową Firebase” wybierz z menu „Wybierz istniejącą aplikację internetową Firebase” skonfigurowaną wcześniej aplikację internetową.
  6. Kliknij „Zakończ i wdróż”. Po chwili wyświetli się nowa strona, na której możesz sprawdzić stan nowego backendu App Hosting.
  7. Po zakończeniu wdrożenia kliknij bezpłatną domenę w sekcji „domains” (domeny). Rozpoczęcie pracy może zająć kilka minut ze względu na propagację DNS.

Pierwsza aplikacja internetowa została wdrożona. Za każdym razem, gdy wypchniesz nowe zatwierdzenie do gałęzi main repozytorium GitHub, w konsoli Firebase rozpocznie się nowa kompilacja i wdrożenie, a Twoja witryna zostanie automatycznie zaktualizowana po zakończeniu wdrażania.

6. Dodaj uwierzytelnianie do aplikacji internetowej

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

Wdrażanie 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 Firebase API:

Interfejs Firebase API

Opis

GoogleAuthProvider

Tworzy instancję dostawcy uwierzytelniania Google.

signInWithPopup

Uruchamia proces uwierzytelniania w oknie dialogowym.

auth.signOut

Wylogowuje użytkownika.

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

  1. Utwórz zatwierdzenie z komunikatem „Dodawanie uwierzytelniania Google” i wypchnij je do repozytorium GitHub. 1. Otwórz stronę App Hosting w konsoli Firebase i poczekaj na zakończenie 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ę powiodło.

Wyślij stan uwierzytelniania do serwera

Do przekazywania stanu 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

Wykorzystamy FirebaseServerApp do powielania stanu uwierzytelniania klienta na serwerze.

Otwórz 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 uwierzytelniania

Aby zasubskrybować zmiany 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 wykorzystuje punkt zaczepienia stanu reakcji, aby aktualizować użytkownika, gdy funkcja onAuthStateChanged określa, że nastąpiła zmiana stanu uwierzytelniania.

Zweryfikuj zmiany

Układ katalogu głównego w pliku src/app/layout.js renderuje nagłówek i przekazuje go użytkownikowi (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 początkowym wczytaniu strony pojawią się zmiany dotyczące uwierzytelniania, moduł obsługi onAuthStateChanged je obsługuje.

Teraz możesz opublikować nową kompilację i sprawdzić, co udało Ci się stworzyć.

  1. Utwórz zatwierdzenie z komunikatem „Pokaż stan logowania” i wypchnij je do repozytorium GitHub.
  2. Otwórz stronę App Hosting w konsoli Firebase i poczekaj na zakończenie 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 się ponownie. Strona aktualizuje się 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 odszukaj wyświetlaną nazwę. Pojawia się on w źródle HTML nieprzetworzonym zwróconym przez serwer.

7. Wyświetl informacje o restauracji

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

Dodaj co najmniej jedną restaurację

Aby wstawić imitację danych restauracji do lokalnej bazy danych Cloud Firestore, wykonaj te czynności:

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

Wyświetl listę 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 sprawdź wywołanie funkcji getRestaurants, która pobiera listę restauracji w czasie działania serwera. Funkcję getRestaurants implementujesz w kolejnych 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 „Odczyt listy restauracji z Firestore” i wypchnij je do repozytorium GitHub.
  2. Otwórz stronę App Hosting w konsoli Firebase i poczekaj na zakończenie wdrożenia.
  3. W aplikacji internetowej odśwież stronę. Zdjęcia restauracji wyświetlają się na stronie jako kafelki.

Sprawdź, czy wizytówki restauracji 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 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 deweloperskie i wyłącz JavaScript.

Wyłącz JavaScript w Narzędziach deweloperskich

  1. Odśwież aplikację internetową. Informacje o restauracjach nadal się wczytują. Informacje o restauracjach są zwracane w odpowiedzi serwera. Przy włączonym JavaScript informacje o restauracjach są przekazywane przez kod JavaScript po stronie klienta.
  2. W Narzędziach deweloperskich ponownie włącz JavaScript.

Nasłuchiwanie aktualizacji restauracji przy użyciu detektorów zrzutów Cloud Firestore

W poprzedniej sekcji omówiliśmy, jak wczytywany jest początkowy zestaw restauracji z pliku src/app/page.js. Plik src/app/page.js jest komponentem serwera i jest renderowany na serwerze wraz z kodem pobierania danych Firebase.

Plik src/components/RestaurantListings.jsx jest komponentem klienckim i można go skonfigurować tak, aby zawierał dane renderowane przez serwer.

Aby skonfigurować plik src/components/RestaurantListings.jsx pod kątem napełniania znaczników renderowanych przez serwer, wykonaj te czynności:

  1. W pliku src/components/RestaurantListings.jsx zaobserwuj ten kod, który jest już napisany w Twoim imieniu:
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, który jest wywoływany po każdym wprowadzeniu zmian w 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 Firestore Database są teraz odzwierciedlane w aplikacji internetowej w czasie rzeczywistym.

  1. Utwórz zatwierdzenie z komunikatem „Nasłuchuj aktualizacji restauracji w czasie rzeczywistym” i wypchnij je do repozytorium na GitHubie.
  2. Otwórz stronę App Hosting w konsoli Firebase i poczekaj na zakończenie wdrożenia.
  3. W aplikacji internetowej kliknij 27ca5d1e8ed8adfe.png > Dodaj przykładowe restauracje. Jeśli funkcja migawki jest poprawnie zaimplementowana, restauracje wyświetlają się w czasie rzeczywistym bez odświeżania strony.

8. Zapisywanie opinii 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 przedstawiający nową opinię. Kod aktualizuje też istniejący dokument Firestore reprezentujący restaurację, dodając zaktualizowane liczby ocen i średnią obliczoną ocenę.

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

Wdrażanie działania serwera Next.js

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

Aby przetworzyć przesłane zgłoszenie za pomocą działania 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ą implementujesz 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ę zaimplementować obsługę przesyłania opinii, więc możesz teraz sprawdzić, czy opinie zostały prawidłowo wstawione 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 je do Twojego repozytorium GitHub.
  2. Otwórz stronę App Hosting w konsoli Firebase i poczekaj na zakończenie 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 pojawi się na początku listy opinii.
  8. W Cloud Firestore wyszukaj w panelu Dodaj dokument dokument z recenzowaną restauracją i wybierz go.
  9. W panelu Rozpocznij kolekcję kliknij 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 dodajesz funkcje, dzięki którym po zalogowaniu możesz zastąpić zdjęcie powiązane z restauracją. Prześlij obraz do Firebase Storage i zaktualizuj jego adres URL w dokumencie Cloud Firestore, który reprezentuje restaurację.

Aby zapisać pliki przesłane przez użytkowników z aplikacji internetowej, wykonaj te czynności:

  1. W pliku src/components/Restaurant.jsx obserwuj 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);
        setRestaurant({ ...restaurant, photo: imageURL });
}

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

  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 zostało przesłane prawidłowo, wykonaj te czynności:

  1. Utwórz zatwierdzenie z komunikatem „Zezwalaj użytkownikom na zmienianie zdjęcia każdej restauracji” i wypchnij je do swojego repozytorium GitHub.
  2. Otwórz stronę App Hosting w konsoli Firebase i poczekaj na zakończenie 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. Przejdź do Cloud Storage dla Firebase.
  6. Przejdź do folderu, który reprezentuje restaurację. Przesłany obraz znajduje się w folderze.

6cf3f9e2303c931c.png

10. Podsumuj opinie o restauracjach za pomocą generatywnej AI

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

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

  1. Aby korzystać z interfejsu Gemini API, potrzebujesz klucza interfejsu API. Utwórz klucz w Google AI Studio.
  2. App Hosting można zintegrować z usługą Cloud Secret Manager, aby umożliwić bezpieczne przechowywanie wartości poufnych, takich jak klucze interfejsu API:
    1. W terminalu uruchom to 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 klucz interfejsu Gemini API z Google AI Studio.
    2. Gdy pojawi się pytanie, czy do apphosting.yaml należy dodać nowy obiekt tajny, wpisz Y, aby zaakceptować.

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

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 zatwierdzenie z komunikatem „Użyj AI do podsumowania opinii” i wypchnij je do repozytorium na GitHubie.
  3. Otwórz stronę App Hosting w konsoli Firebase i poczekaj na zakończenie 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 powinno się zmienić.

11. Podsumowanie

Gratulacje! Wiesz już, jak za pomocą Firebase dodać funkcje do aplikacji Next.js. Najważniejsze jest zagadnienie, które pozwoliło Ci:

Więcej informacji