Firebase in eine Next.js-App einbinden

1. Hinweis

In diesem Codelab erfahren Sie, wie Sie Firebase in eine Next.js-Webanwendung namens Friendly Eats einbinden, eine Website für Restaurantbewertungen.

Friendly Eats-Webanwendung

Die fertige Webanwendung bietet nützliche Funktionen, die zeigen, wie Sie mit Firebase Next.js-Apps erstellen können. Zu diesen Funktionen gehören:

  • Automatischer Build und automatische Bereitstellung:In diesem Codelab wird Firebase App Hosting verwendet, um Ihren Next.js-Code jedes Mal automatisch zu erstellen und bereitzustellen, wenn Sie einen Commit auf einen konfigurierten Branch ausführen.
  • Anmelden und abmelden:In der fertigen Webanwendung können Sie sich mit Google anmelden und abmelden. Die Nutzeranmeldung und ‑speicherung werden vollständig über Firebase Authentication verwaltet.
  • Bilder:In der fertigen Webanwendung können angemeldete Nutzer Bilder von Restaurants hochladen. Bild-Assets werden in Cloud Storage for Firebase gespeichert. Das Firebase JavaScript SDK stellt eine öffentliche URL zu hochgeladenen Bildern bereit. Diese öffentliche URL wird dann im entsprechenden Restaurantdokument in Cloud Firestore gespeichert.
  • Rezensionen:In der fertigen Web-App können angemeldete Nutzer Rezensionen zu Restaurants posten, die aus einer Sternebewertung und einer textbasierten Nachricht bestehen. Rezensionsinformationen werden in Cloud Firestore gespeichert.
  • Filter:In der fertigen Webanwendung können angemeldete Nutzer die Liste der Restaurants nach Kategorie, Standort und Preis filtern. Sie können auch die verwendete Sortiermethode anpassen. Der Zugriff auf die Daten erfolgt über Cloud Firestore und Firestore-Abfragen werden basierend auf den verwendeten Filtern angewendet.

Vorbereitung

  • Ein GitHub-Konto
  • Kenntnisse in Next.js und JavaScript

Lerninhalte

  • Firebase mit dem Next.js App Router und serverseitigem Rendering verwenden
  • Bilder in Cloud Storage for Firebase speichern
  • Daten in einer Cloud Firestore-Datenbank lesen und schreiben
  • Informationen zur Verwendung der Anmeldung über Google mit dem Firebase JavaScript SDK

Voraussetzungen

  • Git
  • Eine aktuelle stabile Version von Node.js
  • Einen Browser Ihrer Wahl, z. B. Google Chrome
  • Eine Entwicklungsumgebung mit einem Code-Editor und einem Terminal
  • Ein Google-Konto zum Erstellen und Verwalten Ihres Firebase-Projekts
  • Sie können Ihr Firebase-Projekt auf den Blaze-Tarif umstellen.

2. Entwicklungsumgebung und GitHub-Repository einrichten

Dieses Codelab enthält den Code der App und verwendet die Firebase CLI.

GitHub-Repository erstellen

Den Codelab-Quellcode finden Sie unter https://github.com/firebase/friendlyeats-web. Das Repository enthält Beispielprojekte für mehrere Plattformen. In diesem Codelab wird jedoch nur das Verzeichnis nextjs-start verwendet. Notieren Sie sich die folgenden Verzeichnisse:

* `nextjs-start`: contains the starter code upon which you build.
* `nextjs-end`: contains the solution code for the finished web app.

Kopieren Sie den Ordner nextjs-start in Ihr eigenes Repository:

  1. Erstellen Sie über ein Terminal einen neuen Ordner auf Ihrem Computer und wechseln Sie in das neue Verzeichnis:
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. Verwenden Sie das npm-Paket giget, um nur den Ordner nextjs-start abzurufen:
    npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
    
  3. Änderungen lokal mit git nachverfolgen:
    git init
    
    git commit -a -m "codelab starting point"
    
    git branch -M main
    
  4. Erstellen Sie ein neues GitHub-Repository: https://github.com/new. Sie können ihm einen beliebigen Namen geben.
    1. GitHub gibt Ihnen eine neue Repository-URL, die entweder https://github.com//.git oder git@github.com:/.git lautet. Kopieren Sie diese URL.
  5. Übertragen Sie lokale Änderungen per Push in Ihr neues GitHub-Repository. Führen Sie den folgenden Befehl aus und ersetzen Sie dabei den Platzhalter durch Ihre Repository-URL.
    git remote add origin <your-repository-url>
    
    git push -u origin main
    
  6. Der Startcode sollte jetzt in Ihrem GitHub-Repository angezeigt werden.

Firebase CLI installieren oder aktualisieren

Führen Sie den folgenden Befehl aus, um zu prüfen, ob die Firebase CLI installiert ist und ob es sich um Version 13.9.0 oder höher handelt:

firebase --version

Wenn eine niedrigere Version angezeigt wird oder die Firebase CLI nicht installiert ist, führen Sie den Installationsbefehl aus:

npm install -g firebase-tools@latest

Wenn Sie die Firebase CLI aufgrund von Berechtigungsfehlern nicht installieren können, lesen Sie die npm-Dokumentation oder verwenden Sie eine andere Installationsoption.

In Firebase anmelden

  1. Führen Sie den folgenden Befehl aus, um sich in der Firebase CLI anzumelden:
    firebase login
    
  2. Geben Sie Y oder N ein, je nachdem, ob Firebase Daten erheben soll.
  3. Wählen Sie in Ihrem Browser Ihr Google-Konto aus und klicken Sie auf Zulassen.

3. Das Firebase-Projekt einrichten

In diesem Abschnitt richten Sie ein Firebase-Projekt ein und verknüpfen es mit einer Firebase-Web-App. Außerdem richten Sie die Firebase-Dienste ein, die von der Beispiel-Web-App verwendet werden.

Firebase-Projekt erstellen

  1. Klicken Sie in der Firebase Console auf Projekt hinzufügen.
  2. Geben Sie im Textfeld Projektnamen eingeben FriendlyEats Codelab (oder einen beliebigen Projektnamen) ein und klicken Sie auf Weiter.
  3. Prüfen Sie im Modalfenster Firebase-Tarif bestätigen, ob der Tarif Blaze ist, und klicken Sie dann auf Tarif bestätigen.
  4. Für dieses Codelab benötigen Sie kein Google Analytics. Deaktivieren Sie daher die Option Google Analytics für dieses Projekt aktivieren.
  5. Klicken Sie auf Projekt erstellen.
  6. Warten Sie, bis Ihr Projekt bereitgestellt wurde, und klicken Sie dann auf Weiter.
  7. Rufen Sie in Ihrem Firebase-Projekt die Projekteinstellungen auf. Notieren Sie sich die Projekt-ID, da Sie sie später benötigen. Anhand dieser eindeutigen Kennung wird Ihr Projekt identifiziert, z. B. in der Firebase CLI.

Firebase-Preismodell upgraden

Wenn Sie Firebase App Hosting und Cloud Storage for Firebase verwenden möchten, muss Ihr Firebase-Projekt den Blaze-Tarif (Pay as you go) haben. Das bedeutet, dass es mit einem Cloud-Rechnungskonto verknüpft sein muss.

So führen Sie ein Upgrade auf den Blaze-Tarif durch:

  1. Wählen Sie in der Firebase Console Tarif upgraden aus.
  2. Wählen Sie den Blaze-Tarif aus. Folgen Sie der Anleitung auf dem Bildschirm, um ein Cloud-Rechnungskonto mit Ihrem Projekt zu verknüpfen.
    Wenn Sie im Rahmen dieses Upgrades ein Cloud-Rechnungskonto erstellen mussten, müssen Sie möglicherweise zur Firebase Console zurückkehren, um das Upgrade abzuschließen.

Ihrem Firebase-Projekt eine Web-App hinzufügen

  1. Rufen Sie in Ihrem Firebase-Projekt die Projektübersicht auf und klicken Sie dann auf e41f2efdd9539c31.png Web.

    Wenn in Ihrem Projekt bereits Apps registriert sind, klicken Sie auf App hinzufügen, um das Web-Symbol zu sehen.
  2. Geben Sie im Textfeld App-Alias einen einprägsamen Alias für die App ein, z. B. My Next.js app.
  3. Lassen Sie das Kästchen Richten Sie außerdem Firebase Hosting für diese App ein deaktiviert.
  4. Klicken Sie auf App registrieren > Weiter > Weiter > Weiter zur Konsole.

Firebase-Dienste in der Firebase Console einrichten

Authentifizierung einrichten

  1. Klicken Sie in der Firebase Console auf Authentifizierung.
  2. Klicken Sie auf Jetzt starten.
  3. Klicken Sie in der Spalte Zusätzliche Anbieter auf Google > Aktivieren.
  4. Geben Sie im Textfeld Öffentlicher Name für Projekt einen einprägsamen Namen wie My Next.js app ein.
  5. Wählen Sie im Drop-down-Menü Support-E-Mail-Adresse für das Projekt Ihre E-Mail-Adresse aus.
  6. Klicken Sie auf Speichern.

Cloud Firestore einrichten

  1. Maximieren Sie im linken Bereich der Firebase Console Build und wählen Sie dann Firestore-Datenbank aus.
  2. Klicken Sie auf Datenbank erstellen.
  3. Lassen Sie die Datenbank-ID auf (default).
  4. Wählen Sie einen Speicherort für die Datenbank aus und klicken Sie auf Weiter.
    Für eine echte App sollten Sie einen Speicherort in der Nähe Ihrer Nutzer auswählen.
  5. Klicken Sie auf Im Testmodus starten. Lesen Sie den Haftungsausschluss zu den Sicherheitsregeln.
    Später in diesem Codelab fügen Sie Sicherheitsregeln hinzu, um Ihre Daten zu schützen. Veröffentlichen oder verteilen Sie keine App, ohne Sicherheitsregeln für Ihre Datenbank hinzuzufügen.
  6. Klicken Sie auf Erstellen.

Cloud Storage for Firebase einrichten

  1. Maximieren Sie im linken Bereich der Firebase Console die Option Build und wählen Sie dann Storage aus.
  2. Klicken Sie auf Jetzt starten.
  3. Wählen Sie einen Speicherort für Ihren Standard-Storage-Bucket aus.
    Für Buckets in US-WEST1, US-CENTRAL1 und US-EAST1 kann die Stufe „Immer kostenlos“ für Google Cloud Storage genutzt werden. Für Buckets an allen anderen Speicherorten gelten die Preise und Nutzungsbedingungen für Google Cloud Storage.
  4. Klicken Sie auf Im Testmodus starten. Lesen Sie den Haftungsausschluss zu den Sicherheitsregeln.
    Später in diesem Codelab fügen Sie Sicherheitsregeln hinzu, um Ihre Daten zu schützen. Veröffentlichen oder verteilen Sie keine App, ohne Sicherheitsregeln für Ihren Speicher-Bucket hinzuzufügen.
  5. Klicken Sie auf Erstellen.

4. Starter-Codebasis ansehen

In diesem Abschnitt sehen Sie sich einige Bereiche der Startcodebasis der App an, denen Sie in diesem Codelab Funktionen hinzufügen.

Ordner- und Dateistruktur

Die folgende Tabelle enthält einen Überblick über die Ordner- und Dateistruktur der App:

Ordner und Dateien

Beschreibung

src/components

React-Komponenten für Filter, Überschriften, Restaurantdetails und Rezensionen

src/lib

Dienstfunktionen, die nicht unbedingt an React oder Next.js gebunden sind

src/lib/firebase

Firebase-spezifischer Code und Firebase-Konfiguration

public

Statische Assets in der Webanwendung, z. B. Symbole

src/app

Routing mit dem Next.js App Router

src/app/restaurant

API-Wege-Handler

package.json und package-lock.json

Projektabhängigkeiten mit npm

next.config.js

Next.js-spezifische Konfiguration (Serveraktionen sind aktiviert)

jsconfig.json

Konfiguration des JavaScript-Sprachdiensts

Server- und Clientkomponenten

Die App ist eine Next.js-Webanwendung, die den App-Router verwendet. Das serverseitige Rendering wird in der gesamten App verwendet. Die Datei src/app/page.js ist beispielsweise eine Serverkomponente, die für die Startseite verantwortlich ist. Die Datei src/components/RestaurantListings.jsx ist eine Clientkomponente, die durch die Anweisung "use client" am Anfang der Datei gekennzeichnet ist.

Importanweisungen

Möglicherweise sehen Sie Importanweisungen wie die folgenden:

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

In der App wird das Symbol @ verwendet, um unübersichtliche relative Importpfade zu vermeiden. Dies wird durch Pfadaliasse ermöglicht.

Firebase-spezifische APIs

Der gesamte Firebase API-Code wird im Verzeichnis src/lib/firebase verpackt. Einzelne React-Komponenten importieren dann die verpackten Funktionen aus dem Verzeichnis src/lib/firebase, anstatt Firebase-Funktionen direkt zu importieren.

Mock-Daten

Die Daten zu fiktiven Restaurants und Rezensionen sind in der Datei src/lib/randomData.js enthalten. Die Daten aus dieser Datei werden im Code in der Datei src/lib/fakeRestaurants.js zusammengeführt.

5. App-Hosting-Backend erstellen

In diesem Abschnitt richten Sie ein App Hosting-Backend ein, um einen Branch in Ihrem Git-Repository zu beobachten.

Am Ende dieses Abschnitts haben Sie ein App Hosting-Backend, das mit Ihrem Repository in GitHub verbunden ist und automatisch eine neue Version Ihrer App neu erstellt und bereitstellt, wenn Sie einen neuen Commit an Ihren main-Branch pushen.

Sicherheitsregeln bereitstellen

Der Code enthält bereits Sicherheitsregeln für Firestore und Cloud Storage for Firebase. Nach der Bereitstellung der Sicherheitsregeln sind die Daten in Ihrer Datenbank und Ihrem Bucket besser vor Missbrauch geschützt.

  1. Konfigurieren Sie in Ihrem Terminal die Befehlszeile, um das zuvor erstellte Firebase-Projekt zu verwenden:
    firebase use --add
    
    Wenn Sie nach einem Alias gefragt werden, geben Sie friendlyeats-codelab ein.
  2. Führen Sie zum Bereitstellen dieser Sicherheitsregeln den folgenden Befehl in Ihrem Terminal aus:
    firebase deploy --only firestore:rules,storage
    
  3. Wenn Sie gefragt werden: "Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?", drücken Sie Enter, um Ja auszuwählen.

Firebase-Konfiguration in den Code Ihrer Webanwendung einfügen

  1. Rufen Sie in der Firebase Console die Projekteinstellungen auf.
  2. Klicken Sie im Bereich SDK-Einrichtung und -Konfiguration auf „App hinzufügen“ und dann auf das Symbol für geschweifte Klammern , um eine neue Webanwendung zu registrieren.
  3. Kopieren Sie am Ende des Workflows zum Erstellen der Webanwendung die Variable firebaseConfig sowie ihre Properties und Werte.
  4. Öffnen Sie die Datei apphosting.yaml in Ihrem Code-Editor und füllen Sie die Werte der Umgebungsvariablen mit den Konfigurationswerten aus der Firebase Console aus.
  5. Ersetzen Sie in der Datei die vorhandenen Properties durch die kopierten.
  6. Speichern Sie die Datei.

Backend erstellen

  1. Rufen Sie in der Firebase Console die Seite App-Hosting auf:

Der Nullzustand der App Hosting Console mit der Schaltfläche „Jetzt starten“

  1. Klicken Sie auf „Jetzt starten“, um mit der Erstellung des Back-Ends zu beginnen. Konfigurieren Sie Ihr Backend so:
  2. Folgen Sie der Anleitung im ersten Schritt, um das zuvor erstellte GitHub-Repository zu verbinden.
  3. Legen Sie die Bereitstellungseinstellungen fest:
    1. Stammverzeichnis bei / belassen
    2. Legen Sie den Live-Zweig auf main fest.
    3. Automatische Roll-outs aktivieren
  4. Benennen Sie das Backend friendlyeats-codelab.
  5. Wählen Sie unter „Firebase-Web-App erstellen oder verknüpfen“ im Drop-down-Menü „Vorhandene Firebase-Web-App auswählen“ die zuvor konfigurierte Web-App aus.
  6. Klicken Sie auf „Fertigstellen und bereitstellen“. Nach einem Moment werden Sie zu einer neuen Seite weitergeleitet, auf der Sie den Status Ihres neuen App-Hosting-Backends sehen.
  7. Klicken Sie nach Abschluss des Roll-outs unter „Domains“ auf Ihre kostenlose Domain. Aufgrund der DNS-Verbreitung kann es einige Minuten dauern, bis die Änderungen wirksam werden.

Sie haben die erste Webanwendung bereitgestellt. Jedes Mal, wenn Sie einen neuen Commit in den main-Branch Ihres GitHub-Repositories pushen, wird in der Firebase Console ein neuer Build und ein neues Roll-out gestartet. Ihre Website wird automatisch aktualisiert, sobald das Roll-out abgeschlossen ist.

6. Authentifizierung zur Webanwendung hinzufügen

In diesem Abschnitt fügen Sie der Webanwendung eine Authentifizierung hinzu, damit Sie sich darin anmelden können.

Anmelde- und Abmeldefunktionen implementieren

  1. Ersetzen Sie in der Datei src/lib/firebase/auth.js die Funktionen onAuthStateChanged, signInWithGoogle und signOut durch den folgenden Code:
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);
  }
}

In diesem Code werden die folgenden Firebase APIs verwendet:

Firebase API

Beschreibung

GoogleAuthProvider

Erstellt eine Instanz des Google-Authentifizierungsanbieters.

signInWithPopup

Startet einen dialogbasierten Authentifizierungsablauf.

auth.signOut

Der Nutzer wird abgemeldet.

In der Datei src/components/Header.jsx ruft der Code bereits die Funktionen signInWithGoogle und signOut auf.

  1. Erstellen Sie einen Commit mit der Commit-Nachricht „Google-Authentifizierung hinzufügen“ und pushen Sie ihn in Ihr GitHub-Repository. 1. Öffnen Sie in der Firebase Console die Seite App-Hosting und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  2. Aktualisieren Sie die Seite in der Webanwendung und klicken Sie auf Über Google anmelden. Die Webanwendung wird nicht aktualisiert, sodass nicht klar ist, ob die Anmeldung erfolgreich war.

Authentifizierungsstatus an den Server senden

Um den Authentifizierungsstatus an den Server weiterzugeben, verwenden wir einen Dienst-Worker. Ersetzen Sie die Funktionen fetchWithFirebaseHeaders und getAuthIdToken durch den folgenden Code:

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

Authentifizierungsstatus auf dem Server lesen

Wir verwenden FirebaseServerApp, um den Authentifizierungsstatus des Clients auf dem Server zu spiegeln.

Öffnen Sie src/lib/firebase/serverApp.js und ersetzen Sie die Funktion 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 };
}

Benachrichtigungen zu Authentifizierungsänderungen abonnieren

So abonnieren Sie Authentifizierungsänderungen:

  1. Rufen Sie die Datei src/components/Header.jsx auf.
  2. Ersetzen Sie die Funktion useUserSession durch den folgenden Code:
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;
}

In diesem Code wird ein State-Hook von React verwendet, um den Nutzer zu aktualisieren, wenn die onAuthStateChanged-Funktion angibt, dass sich der Authentifizierungsstatus geändert hat.

Änderungen prüfen

Das Stamm-Layout in der src/app/layout.js-Datei rendert den Header und gibt den Nutzer, falls verfügbar, als Prop weiter.

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

Das bedeutet, dass die <Header>-Komponente Nutzerdaten, sofern verfügbar, während der Serverlaufzeit rendert. Wenn nach dem ersten Seitenaufbau während des Seitenlebenszyklus Authentifizierungsaktualisierungen auftreten, werden diese vom onAuthStateChanged-Handler verarbeitet.

Jetzt ist es an der Zeit, einen neuen Build einzuführen und das Ergebnis zu überprüfen.

  1. Erstellen Sie einen Commit mit der Commit-Nachricht „Anmeldestatus anzeigen“ und übertragen Sie ihn per Push in Ihr GitHub-Repository.
  2. Öffnen Sie in der Firebase Console die Seite App-Hosting und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  3. Prüfen Sie das neue Authentifizierungsverhalten:
    1. Aktualisieren Sie die Webanwendung in Ihrem Browser. Ihr Anzeigename wird im Header angezeigt.
    2. Melden Sie sich ab und wieder an. Die Seite wird in Echtzeit aktualisiert, ohne dass sie neu geladen werden muss. Sie können diesen Schritt mit anderen Nutzern wiederholen.
    3. Optional: Klicken Sie mit der rechten Maustaste auf die Webanwendung, wählen Sie Seitenquelltext anzeigen aus und suchen Sie nach dem Anzeigenamen. Sie wird in der Roh-HTML-Quelle angezeigt, die vom Server zurückgegeben wird.

7. Restaurantinformationen ansehen

Die Webanwendung enthält Mock-Daten für Restaurants und Rezensionen.

Mindestens ein Restaurant hinzufügen

So fügen Sie Ihrer lokalen Cloud Firestore-Datenbank Mock-Restaurantdaten hinzu:

  1. Wählen Sie in der Webanwendung 2cf67d488d8e6332.png > Beispielrestaurants hinzufügen aus.
  2. Wählen Sie in der Firebase Console auf der Seite Firestore-Datenbank die Option restaurants aus. Sie sehen die Dokumente der obersten Ebene in der Sammlung „Restaurants“, die jeweils ein Restaurant repräsentieren.
  3. Klicken Sie auf einige Dokumente, um die Eigenschaften eines Restaurantdokuments zu sehen.

Liste der Restaurants anzeigen

Ihre Cloud Firestore-Datenbank enthält jetzt Restaurants, die in der Next.js-Web-App angezeigt werden können.

So definieren Sie den Code zum Abrufen von Daten:

  1. Suchen Sie in der Datei src/app/page.js nach der Serverkomponente <Home /> und prüfen Sie den Aufruf der Funktion getRestaurants, die bei der Serverausführung eine Liste von Restaurants abruft. In den folgenden Schritten implementieren Sie die Funktion getRestaurants.
  2. Ersetzen Sie in der Datei src/lib/firebase/firestore.js die Funktionen applyQueryFilters und getRestaurants durch den folgenden Code:
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. Erstellen Sie einen Commit mit der Commit-Nachricht „Liste der Restaurants aus Firestore lesen“ und pushen Sie ihn in Ihr GitHub-Repository.
  2. Öffnen Sie in der Firebase Console die Seite App-Hosting und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  3. Aktualisieren Sie die Seite in der Webanwendung. Restaurantbilder werden auf der Seite als Kacheln angezeigt.

Prüfen, ob die Restauranteinträge zur Laufzeit des Servers geladen werden

Wenn Sie das Next.js-Framework verwenden, ist möglicherweise nicht klar, ob Daten zur Laufzeit auf dem Server oder clientseitig geladen werden.

So prüfen Sie, ob Restauranteinträge zur Laufzeit des Servers geladen werden:

  1. Öffnen Sie in der Webanwendung die Entwicklertools und deaktivieren Sie JavaScript.

JavaScript in den Entwicklertools deaktivieren

  1. Aktualisieren Sie die Webanwendung. Die Restauranteinträge werden weiterhin geladen. Die Restaurantinformationen werden in der Serverantwort zurückgegeben. Wenn JavaScript aktiviert ist, werden die Restaurantinformationen über den clientseitigen JavaScript-Code animiert.
  2. Aktivieren Sie JavaScript in den Entwicklertools wieder.

Mit Cloud Firestore-Snapshot-Listenern auf Restaurantaktualisierungen warten

Im vorherigen Abschnitt haben Sie gesehen, wie die ersten Restaurants aus der Datei src/app/page.js geladen wurden. Die src/app/page.js-Datei ist eine Serverkomponente und wird auf dem Server gerendert, einschließlich des Firebase-Codes zum Abrufen von Daten.

Die src/components/RestaurantListings.jsx-Datei ist eine Clientkomponente und kann so konfiguriert werden, dass serverseitig gerendertes Markup hydratisiert wird.

So konfigurierst du die src/components/RestaurantListings.jsx-Datei für die Hydratisierung serverseitig gerenderten Markups:

  1. Sehen Sie sich in der Datei src/components/RestaurantListings.jsx den folgenden Code an, der bereits für Sie geschrieben wurde:
useEffect(() => {
        const unsubscribe = getRestaurantsSnapshot(data => {
                setRestaurants(data);
        }, filters);

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

Dieser Code ruft die Funktion getRestaurantsSnapshot() auf, die der Funktion getRestaurants() ähnelt, die Sie in einem vorherigen Schritt implementiert haben. Diese Snapshot-Funktion bietet jedoch einen Callback-Mechanismus, sodass der Callback jedes Mal aufgerufen wird, wenn eine Änderung an der Sammlung des Restaurants vorgenommen wird.

  1. Ersetzen Sie in der Datei src/lib/firebase/firestore.js die Funktion getRestaurantsSnapshot() durch den folgenden Code:
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;
}

Änderungen, die über die Seite Firestore-Datenbank vorgenommen werden, werden jetzt in Echtzeit in der Webanwendung angezeigt.

  1. Erstellen Sie einen Commit mit der Commit-Nachricht „Für Echtzeit-Updates zu Restaurants zuhören“ und pushen Sie ihn in Ihr GitHub-Repository.
  2. Öffnen Sie in der Firebase Console die Seite App-Hosting und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  3. Wählen Sie in der Webanwendung 27ca5d1e8ed8adfe.png > Beispielrestaurants hinzufügen aus. Wenn die Snapshot-Funktion richtig implementiert ist, werden die Restaurants ohne Aktualisierung der Seite in Echtzeit angezeigt.

8. Von Nutzern eingereichte Rezensionen aus der Webanwendung speichern

  1. Ersetzen Sie in der Datei src/lib/firebase/firestore.js die Funktion updateWithRating() durch den folgenden Code:
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()),
	});
};

Mit diesem Code wird ein neues Firestore-Dokument eingefügt, das die neue Rezension darstellt. Außerdem aktualisiert der Code das vorhandene Firestore-Dokument, das das Restaurant darstellt, mit aktualisierten Zahlen für die Anzahl der Bewertungen und die durchschnittlich berechnete Bewertung.

  1. Ersetzen Sie die Funktion addReviewToRestaurant() durch den folgenden Code:
export async function addReviewToRestaurant(db, restaurantId, review) {
	if (!restaurantId) {
		throw new Error("No restaurant ID has been provided.");
	}

	if (!review) {
		throw new Error("A valid review has not been provided.");
	}

	try {
		const docRef = doc(collection(db, "restaurants"), restaurantId);
		const newRatingDocument = doc(
			collection(db, `restaurants/${restaurantId}/ratings`)
		);

		// corrected line
		await runTransaction(db, transaction =>
			updateWithRating(transaction, docRef, newRatingDocument, review)
		);
	} catch (error) {
		console.error(
			"There was an error adding the rating to the restaurant",
			error
		);
		throw error;
	}
}

Next.js-Serveraktion implementieren

Eine Next.js-Serveraktion bietet eine praktische API für den Zugriff auf Formulardaten, z. B. data.get("text"), um den Textwert aus der Formulareinreichungsnutzlast abzurufen.

So verwenden Sie eine Next.js-Serveraktion, um die Einreichung des Formulars zur Überprüfung zu verarbeiten:

  1. Suchen Sie in der Datei src/components/ReviewDialog.jsx im Element <form> nach dem Attribut action.
<form action={handleReviewFormSubmission}>

Der action-Attributwert bezieht sich auf eine Funktion, die Sie im nächsten Schritt implementieren.

  1. Ersetzen Sie in der Datei src/app/actions.js die Funktion handleReviewFormSubmission() durch den folgenden Code:
// 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"),
        });
}

Rezensionen für ein Restaurant hinzufügen

Sie haben die Unterstützung für die Einreichung von Rezensionen implementiert. Jetzt können Sie prüfen, ob Ihre Rezensionen korrekt in Cloud Firestore eingefügt werden.

So fügen Sie eine Rezension hinzu und prüfen, ob sie in Cloud Firestore eingefügt wurde:

  1. Erstellen Sie einen Commit mit der Commit-Nachricht „Nutzer dürfen Restaurantbewertungen einreichen“ und pushen Sie ihn in Ihr GitHub-Repository.
  2. Öffnen Sie in der Firebase Console die Seite App-Hosting und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  3. Aktualisieren Sie die Webanwendung und wählen Sie auf der Startseite ein Restaurant aus.
  4. Klicken Sie auf der Seite des Restaurants auf 3e19beef78bb0d0e.png.
  5. Wählen Sie eine Bewertung aus.
  6. Rezension schreiben
  7. Klicken Sie auf Senden. Ihre Rezension wird oben in der Liste der Rezensionen angezeigt.
  8. Suchen Sie in Cloud Firestore im Bereich Dokument hinzufügen nach dem Dokument des von Ihnen rezensierten Restaurants und wählen Sie es aus.
  9. Wählen Sie im Bereich Sammlung starten die Option Bewertungen aus.
  10. Suchen Sie im Bereich Dokument hinzufügen nach dem Dokument, um zu prüfen, ob es wie erwartet eingefügt wurde.

Dokumente im Firestore-Emulator

9. Von Nutzern hochgeladene Dateien aus der Webanwendung speichern

In diesem Abschnitt fügen Sie eine Funktion hinzu, mit der Sie das mit einem Restaurant verknüpfte Bild ersetzen können, wenn Sie angemeldet sind. Sie laden das Bild in Firebase Storage hoch und aktualisieren die Bild-URL im Cloud Firestore-Dokument, das das Restaurant darstellt.

So speichern Sie von Nutzern hochgeladene Dateien aus der Webanwendung:

  1. Sehen Sie sich in der Datei src/components/Restaurant.jsx den Code an, der ausgeführt wird, wenn der Nutzer eine Datei hochlädt:
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 });
}

Es sind keine Änderungen erforderlich. Sie implementieren das Verhalten der updateRestaurantImage()-Funktion jedoch in den folgenden Schritten.

  1. Ersetzen Sie in der Datei src/lib/firebase/storage.js die Funktionen updateRestaurantImage() und uploadImage() durch den folgenden Code:
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);
}

Die updateRestaurantImageReference()-Funktion ist bereits für Sie implementiert. Mit dieser Funktion wird ein vorhandenes Restaurantdokument in Cloud Firestore mit einer aktualisierten Bild-URL aktualisiert.

Funktion zum Hochladen von Bildern prüfen

So prüfen Sie, ob das Bild wie erwartet hochgeladen wird:

  1. Erstellen Sie einen Commit mit der Commit-Nachricht „Nutzer dürfen das Foto jedes Restaurants ändern“ und übertragen Sie ihn in Ihr GitHub-Repository.
  2. Öffnen Sie in der Firebase Console die Seite App-Hosting und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  3. Prüfen Sie in der Web-App, ob Sie angemeldet sind, und wählen Sie ein Restaurant aus.
  4. Klicken Sie auf 7067eb41fea41ff0.png und laden Sie ein Bild aus Ihrem Dateisystem hoch. Das Bild verlässt Ihre lokale Umgebung und wird in Cloud Storage hochgeladen. Das Bild wird sofort nach dem Upload angezeigt.
  5. Rufen Sie Cloud Storage for Firebase auf.
  6. Rufen Sie den Ordner auf, der das Restaurant darstellt. Das von Ihnen hochgeladene Bild befindet sich im Ordner.

6cf3f9e2303c931c.png

10. Restaurantbewertungen mit generativer KI zusammenfassen

In diesem Abschnitt fügen Sie eine Funktion für Rezensionszusammenfassungen hinzu, damit Nutzer schnell sehen können, was andere von einem Restaurant halten, ohne jede Rezension lesen zu müssen.

Gemini API-Schlüssel in Cloud Secret Manager speichern

  1. Zur Verwendung der Gemini API benötigen Sie einen API-Schlüssel. Erstellen Sie einen Schlüssel in Google AI Studio.
  2. App Hosting lässt sich in Cloud Secret Manager integrieren, damit Sie vertrauliche Werte wie API-Schlüssel sicher speichern können:
    1. Führen Sie in einem Terminal den folgenden Befehl aus, um ein neues Secret zu erstellen:
    firebase apphosting:secrets:set gemini-api-key
    
    1. Wenn Sie nach dem Secret-Wert gefragt werden, kopieren Sie Ihren Gemini API-Schlüssel aus Google AI Studio und fügen Sie ihn ein.
    2. Wenn Sie gefragt werden, ob das neue Secret zu apphosting.yaml hinzugefügt werden soll, geben Sie Y ein, um zu bestätigen.

Ihr Gemini API-Schlüssel wird jetzt sicher in Cloud Secret Manager gespeichert und ist für Ihr App-Hosting-Backend zugänglich.

Komponente „Rezensionszusammenfassung“ implementieren

  1. Ersetzen Sie in src/components/Reviews/ReviewSummary.jsx die Funktion GeminiSummary durch den folgenden Code:
    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. Erstellen Sie einen Commit mit der Commit-Nachricht „Mit KI Rezensionen zusammenfassen“ und pushen Sie ihn in Ihr GitHub-Repository.
  3. Öffnen Sie in der Firebase Console die Seite App-Hosting und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  4. Öffnen Sie eine Seite für ein Restaurant. Oben sollte eine Zusammenfassung aller Rezensionen auf der Seite in einem Satz angezeigt werden.
  5. Fügen Sie eine neue Rezension hinzu und aktualisieren Sie die Seite. Die Zusammenfassung sollte sich ändern.

11. Fazit

Glückwunsch! Sie haben gelernt, wie Sie mit Firebase einer Next.js-App Funktionen hinzufügen. Dazu haben Sie Folgendes verwendet:

Weitere Informationen