Firebase in eine Next.js-App einbinden

1. Hinweis

In diesem Codelab erfahren Sie, wie Sie Firebase in eine Next.js-Web-App namens Friendly Eats integrieren. Dabei handelt es sich um eine Website für Restaurantrezensionen.

Friendly Eats-Web-App

Die fertige Web-App bietet nützliche Funktionen, die zeigen, wie Firebase Ihnen beim Erstellen von Next.js-Apps helfen kann. Unter anderem sind folgende Funktionen verfügbar:

  • Automatisches Erstellen und Bereitstellen:In diesem Codelab wird Firebase App Hosting verwendet, um Ihren Next.js-Code automatisch zu erstellen und bereitzustellen, wenn Sie ihn in einen konfigurierten Branch übertragen.
  • An- und Abmeldung:Mit der fertigen Webanwendung können Sie sich mit Google anmelden und abmelden. Nutzer-Log‑ins und ‑Persistenz werden vollständig über Firebase Authentication verwaltet.
  • Bilder:In der fertigen Web-App können angemeldete Nutzer Restaurantbilder hochladen. Bild-Assets werden in Cloud Storage for Firebase gespeichert. Das Firebase JavaScript SDK stellt eine öffentliche URL für hochgeladene Bilder bereit. Diese öffentliche URL wird dann im entsprechenden Restaurantdokument in Cloud Firestore gespeichert.
  • Rezensionen:Mit der fertigen Web-App können angemeldete Nutzer Rezensionen von Restaurants in Form einer Sternebewertung und einer Textnachricht posten. Rezensionsinformationen werden in Cloud Firestore gespeichert.
  • Filter:In der fertigen Web-App können angemeldete Nutzer die Liste der Restaurants nach Kategorie, Standort und Preis filtern. Sie können auch die verwendete Sortiermethode anpassen. Auf Daten wird über Cloud Firestore zugegriffen 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
  • So lesen und schreiben Sie Daten in einer Cloud Firestore-Datenbank.
  • So verwenden Sie die Anmeldung mit 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 upgraden.

2. Entwicklungsumgebung und GitHub-Repository einrichten

Dieses Codelab enthält den Starter-Code der App und basiert auf der Firebase CLI.

GitHub-Repository erstellen

Den Quellcode für das Codelab 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. Beachten Sie 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 add .
    
    git commit -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.
  5. Kopieren Sie die neue URL, die GitHub für Sie erstellt. Sie sieht so aus:
    • https://github.com/<USER_NAME>/<REPOSITORY_NAME>.git oder
    • git@github.com:<USER_NAME>/<REPOSITORY_NAME>.git
  6. Übertragen Sie lokale Änderungen per Push in Ihr neues GitHub-Repository, indem Sie den folgenden Befehl ausführen. Ersetzen Sie den Platzhalter <REPOSITORY_URL> durch die tatsächliche Repository-URL.
    git remote add origin <REPOSITORY_URL>
    
    git push -u origin main
    
  7. Jetzt sollte der Startcode 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 14.1.0 oder höher handelt:

firebase --version

Wenn Sie eine niedrigere Version sehen 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 dann auf Zulassen.

3. Das Firebase-Projekt einrichten

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

Firebase-Projekt erstellen

  1. Melden Sie sich in der Firebase-Konsole mit demselben Google-Konto an, das Sie im vorherigen Schritt verwendet haben.
  2. Klicken Sie auf die Schaltfläche, um ein neues Projekt zu erstellen, und geben Sie dann einen Projektnamen ein (z. B. FriendlyEats Codelab).
  3. Klicken Sie auf Weiter.
  4. Lesen und akzeptieren Sie bei Aufforderung die Firebase-Nutzungsbedingungen und klicken Sie dann auf Weiter.
  5. (Optional) Aktivieren Sie die KI-Unterstützung in der Firebase Console (als „Gemini in Firebase“ bezeichnet).
  6. Für dieses Codelab benötigen Sie kein Google Analytics. Deaktivieren Sie daher die Google Analytics-Option.
  7. Klicken Sie auf Projekt erstellen, warten Sie, bis Ihr Projekt bereitgestellt wurde, und klicken Sie dann auf Weiter.

Firebase-Tarif 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) nutzen. Das bedeutet, dass es mit einem Cloud-Rechnungskonto verknüpft ist.

  • Für ein Cloud-Rechnungskonto ist eine Zahlungsmethode wie eine Kreditkarte erforderlich.
  • Wenn Sie neu bei Firebase und Google Cloud sind, können Sie prüfen, ob Sie Anspruch auf ein Guthaben von 300$und ein Cloud-Rechnungskonto für den kostenlosen Testzeitraum haben.
  • Wenn Sie dieses Codelab im Rahmen einer Veranstaltung durchführen, fragen Sie den Organisator, ob Cloud-Guthaben verfügbar ist.

So führen Sie für Ihr Projekt ein Upgrade auf den Tarif „Blaze“ durch:

  1. Wählen Sie in der Firebase Console die Option zum Upgraden Ihres Abos 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-Konsole zurückkehren, um das Upgrade abzuschließen.

Web-App zu Ihrem Firebase-Projekt 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 Websymbol zu sehen.
  2. Geben Sie im Textfeld App-Nickname einen einprägsamen App-Nickname ein, z. B. My Next.js app.
  3. Lassen Sie das Kästchen Firebase Hosting für diese App einrichten deaktiviert.
  4. Klicken Sie auf App registrieren > Weiter zur Konsole.

Firebase-Dienste in der Firebase Console einrichten

Authentifizierung einrichten

  1. Rufen Sie in der Firebase Console Authentifizierung auf.
  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 ein, z. B. My Next.js app.
  5. Wählen Sie im Drop-down-Menü Support-E-Mail-Adresse für 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. Belassen Sie die Database ID (Datenbank-ID) auf (default).
  4. Wählen Sie einen Speicherort für Ihre Datenbank aus und klicken Sie auf Weiter.
    Für eine echte App sollten Sie einen Speicherort auswählen, der sich in der Nähe Ihrer Nutzer befindet.
  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 Sie eine App nicht öffentlich, 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 Build und wählen Sie dann Storage aus.
  2. Klicken Sie auf Jetzt starten.
  3. Wählen Sie einen Standort für Ihren standardmäßigen Storage-Bucket aus.
    Für Buckets in US-WEST1, US-CENTRAL1 und US-EAST1 kann die kostenlose Stufe für Google Cloud Storage genutzt werden. Für Buckets an allen anderen Standorten gelten die Preise und die Nutzung von 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 Sie keine App öffentlich, ohne Sicherheitsregeln für Ihren Storage-Bucket hinzuzufügen.
  5. Klicken Sie auf Erstellen.

Sicherheitsregeln bereitstellen

Der Code enthält bereits Gruppen von Sicherheitsregeln für Firestore und Cloud Storage for Firebase. Nachdem Sie die Sicherheitsregeln bereitgestellt haben, sind die Daten in Ihrer Datenbank und Ihrem Bucket besser vor Missbrauch geschützt.

  1. Konfigurieren Sie in Ihrem Terminal die CLI, um das Firebase-Projekt zu verwenden, das Sie zuvor erstellt haben:
    firebase use --add
    
    Geben Sie bei Aufforderung friendlyeats-codelab als Alias ein.
  2. Führen Sie den folgenden Befehl in Ihrem Terminal aus, um diese Sicherheitsregeln (sowie Indexe, die später benötigt werden) bereitzustellen:
    firebase deploy --only firestore,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.

4. Starter-Codebasis prüfen

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

Ordner- und Dateistruktur

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

Ordner und Dateien

Beschreibung

src/components

React-Komponenten für Filter, Kopfzeilen, Restaurantdetails und Rezensionen

src/lib

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

src/lib/firebase

Firebase-spezifischer Code und Firebase-Konfiguration

public

Statische Assets in der Web-App, z. B. Symbole

src/app

Routing mit dem Next.js-App-Router

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-Sprachdienstes

Server- und Clientkomponenten

Die App ist eine Next.js-Webanwendung, die den App Router verwendet. Serverseitiges Rendering wird in der gesamten App verwendet. Die Datei src/app/page.js ist beispielsweise eine Serverkomponente, die für die Hauptseite zuständig 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 umständliche relative Importpfade zu vermeiden. Dies wird durch Pfad-Aliasse ermöglicht.

Firebase-spezifische APIs

Der gesamte Firebase API-Code befindet sich im Verzeichnis src/lib/firebase. Die einzelnen React-Komponenten importieren die umschlossenen Funktionen dann aus dem Verzeichnis src/lib/firebase, anstatt Firebase-Funktionen direkt zu importieren.

Simulierte Daten

Die Datei src/lib/randomData.js enthält Mock-Daten für Restaurants und Rezensionen. Die Daten aus dieser Datei werden im Code in der Datei src/lib/fakeRestaurants.js zusammengestellt.

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. Es wird automatisch neu erstellt und eine neue Version Ihrer App wird bereitgestellt, wenn Sie einen neuen Commit in Ihren main-Branch übertragen.

Backend erstellen

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

Der Nullstatus der App Hosting Console mit der Schaltfläche „Erste Schritte“

  1. Klicken Sie auf „Jetzt starten“, um den Erstellungsvorgang für das Back-End zu starten. Konfigurieren Sie Ihr Backend so:
  2. Wählen Sie eine Region aus. Bei einer echten App würden Sie die Region auswählen, die Ihren Nutzern am nächsten ist.
  3. Folgen Sie der Anleitung im Schritt „GitHub-Repository importieren“, um das GitHub-Repository zu verbinden, das Sie zuvor erstellt haben.
  4. Bereitstellungseinstellungen festlegen:
    1. Behalten Sie das Stammverzeichnis als / bei.
    2. Stelle den Live-Zweig auf main ein.
    3. Automatische Roll-outs aktivieren
  5. Benennen Sie das Backend mit friendlyeats-codelab.
  6. Klicken Sie unter „Firebase-Web-App verknüpfen“ auf „Neue Firebase-Web-App erstellen“.
  7. Klicken Sie auf „Fertigstellen und bereitstellen“. Nach kurzer Zeit werden Sie zu einer neuen Seite weitergeleitet, auf der Sie den Status Ihres neuen App Hosting-Back-Ends sehen können.
  8. Klicken Sie nach Abschluss der Einführung unter „Domains“ auf Ihre kostenlose Domain. Es kann einige Minuten dauern, bis die Änderungen aufgrund der DNS-Weitergabe wirksam werden.
  9. Hoppla! Beim Laden der Seite wird die Fehlermeldung „Anwendungsfehler: Es ist eine serverseitige Ausnahme aufgetreten (weitere Informationen finden Sie in den Serverlogs).“ angezeigt.
  10. Sehen Sie in der Firebase Console auf dem Tab „Logs“ (Protokolle) Ihres App Hosting-Backends nach. Sie sehen ein Log mit dem Hinweis „Error: not implemented“ (Fehler: nicht implementiert). Das beheben wir im nächsten Schritt, wenn wir die Authentifizierung hinzufügen.

Sie haben die erste Web-App bereitgestellt. Jedes Mal, wenn Sie einen neuen Commit in den main-Branch Ihres GitHub-Repositorys übertragen, wird in der Firebase-Konsole ein neuer Build und Roll-out gestartet. Ihre Website wird automatisch aktualisiert, sobald der Roll-out abgeschlossen ist.

6. Authentifizierung zur Web-App hinzufügen

In diesem Abschnitt fügen Sie der Web-App die Authentifizierung hinzu, damit Sie sich anmelden können.

Fügen Sie eine autorisierte Domain hinzu

Firebase Authentication akzeptiert nur Anmeldeanfragen von Domains, die Sie zulassen. Hier fügen wir die Domain des App-Hosting-Back-Ends zur Liste der genehmigten Domains in Ihrem Projekt hinzu.

  1. Kopieren Sie die Domain Ihres App Hosting-Backends von der Seite „Übersicht“ von App Hosting.
  2. Rufen Sie den Tab Auth Settings auf und wählen Sie Authorized Domains (Autorisierte Domains) aus.
  3. Klicken Sie auf Domain hinzufügen.
  4. Geben Sie die Domain Ihres App Hosting-Backends ein.
  5. Klicken Sie auf Hinzufügen.

An- und Abmeldefunktionen implementieren

  1. Ersetzen Sie in der Datei src/lib/firebase/auth.js die Funktionen onAuthStateChanged, onIdTokenChanged, signInWithGoogle und signOut durch den folgenden Code:
export function onAuthStateChanged(cb) {
  return _onAuthStateChanged(auth, cb);
}

export function onIdTokenChanged(cb) {
  return _onIdTokenChanged(auth, cb);
}

export async function signInWithGoogle() {
  const provider = new GoogleAuthProvider();

  try {
    await signInWithPopup(auth, provider);
  } catch (error) {
    console.error("Error signing in with Google", error);
  }
}

export async function signOut() {
  try {
    return auth.signOut();
  } catch (error) {
    console.error("Error signing out with Google", error);
  }
}

In diesem Code werden die folgenden Firebase APIs verwendet:

Firebase API

Beschreibung

auth.onAuthStateChanged

Fügt einen Observer für Änderungen am Anmeldestatus des Nutzers hinzu.

auth.onIdTokenChanged

Fügt einen Observer für Änderungen am ID-Token des Nutzers hinzu.

GoogleAuthProvider

Erstellt eine Instanz des Google-Authentifizierungsanbieters.

signInWithPopup

Startet einen dialogbasierten Authentifizierungsvorgang.

auth.signOut

Meldet den Nutzer ab.

Im Code der Datei src/components/Header.jsx werden die Funktionen signInWithGoogle und signOut bereits aufgerufen.

Authentifizierungsstatus an den Server senden

Um den Authentifizierungsstatus an den Server zu übergeben, verwenden wir Cookies. Immer wenn sich der Authentifizierungsstatus im Client ändert, aktualisieren wir das __session-Cookie.

Ersetzen Sie in src/components/Header.jsx die Funktion useUserSession durch den folgenden Code:

function useUserSession(initialUser) {
  useEffect(() => {
    return onIdTokenChanged(async (user) => {
      if (user) {
        const idToken = await user.getIdToken();
        await setCookie("__session", idToken);
      } else {
        await deleteCookie("__session");
      }
      if (initialUser?.uid === user?.uid) {
        return;
      }
      window.location.reload();
    });
  }, [initialUser]);

  return initialUser;
}

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 authIdToken = (await cookies()).get("__session")?.value;

  // Firebase Server App is a new feature in the JS SDK that allows you to
  // instantiate the SDK with credentials retrieved from the client & has
  // other affordances for use in server environments.
  const firebaseServerApp = initializeServerApp(
    // https://github.com/firebase/firebase-js-sdk/issues/8863#issuecomment-2751401913
    initializeApp(),
    {
      authIdToken,
    }
  );

  const auth = getAuth(firebaseServerApp);
  await auth.authStateReady();

  return { firebaseServerApp, currentUser: auth.currentUser };
}

Änderungen prüfen

Im Root-Layout in der Datei src/app/layout.js wird der Header gerendert und der Nutzer, falls verfügbar, als Attribut übergeben.

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

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

Jetzt ist es an der Zeit, einen neuen Build bereitzustellen und zu überprüfen, was Sie erstellt haben.

  1. Erstellen Sie einen Commit mit der Commit-Nachricht „Add authentication“ (Authentifizierung hinzufügen) und übertragen Sie ihn per Push an Ihr GitHub-Repository.
  2. Öffnen Sie in der Firebase Console die App Hosting-Seite und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  3. Neues Authentifizierungsverhalten überprüfen:
    1. Aktualisieren Sie die Web-App in Ihrem Browser. Ihr Anzeigename wird im Header angezeigt.
    2. Melden Sie sich ab und wieder an. Sie können diesen Schritt mit verschiedenen 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. Es ist im Roh-HTML-Quellcode enthalten, der vom Server zurückgegeben wird.

7. Restaurantinformationen ansehen

Die Web-App enthält Beispieldaten für Restaurants und Rezensionen.

Ein oder mehrere Restaurants hinzufügen

So fügen Sie Mock-Restaurantdaten in Ihre lokale Cloud Firestore-Datenbank ein:

  1. Melden Sie sich in der Web-App an, falls noch nicht geschehen. Wählen Sie dann 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 Attribute 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 sehen Sie sich den Aufruf der Funktion getRestaurants an, mit der zur Laufzeit des Servers eine Liste von Restaurants abgerufen wird. 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 „Read the list of restaurants from Firestore“ (Liste der Restaurants aus Firestore lesen) und übertragen Sie ihn per Push in Ihr GitHub-Repository.
  2. Öffnen Sie in der Firebase Console die App Hosting-Seite und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  3. Aktualisieren Sie die Seite in der Webanwendung. Restaurantbilder werden als Kacheln auf der Seite angezeigt.

Prüfen, ob die Restaurantinformationen beim Ausführen des Servers geladen werden

Wenn Sie das Next.js-Framework verwenden, ist es möglicherweise nicht offensichtlich, wann Daten zur Serverlaufzeit oder zur clientseitigen Laufzeit geladen werden.

So prüfen Sie, ob Restaurant-Einträge zur Serverlaufzeit geladen werden:

  1. Öffnen Sie in der Web-App die Entwicklertools und deaktivieren Sie JavaScript.

JavaScript in den Entwicklertools deaktivieren

  1. Aktualisieren Sie die Web-App. Die Restaurantinformationen werden weiterhin geladen. Restaurantinformationen werden in der Serverantwort zurückgegeben. Wenn JavaScript aktiviert ist, werden die Restaurantinformationen über den clientseitigen JavaScript-Code bereitgestellt.
  2. Aktivieren Sie JavaScript in den Entwicklertools wieder.

Mit Cloud Firestore-Snapshot-Listenern auf Restaurantaktualisierungen warten

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

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

So konfigurieren Sie die Datei src/components/RestaurantListings.jsx, um serverseitig gerendertes Markup zu rehydrieren:

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

Mit diesem Code wird die Funktion getRestaurantsSnapshot() aufgerufen, 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);

  return onSnapshot(q, (querySnapshot) => {
    const results = querySnapshot.docs.map((doc) => {
      return {
        id: doc.id,
        ...doc.data(),
        // Only plain objects can be passed to Client Components from Server Components
        timestamp: doc.data().timestamp.toDate(),
      };
    });

    cb(results);
  });
}

Änderungen, die über die Seite Firestore Database vorgenommen werden, werden jetzt in Echtzeit in der Web-App angezeigt.

  1. Erstellen Sie einen Commit mit der Commit-Nachricht „Listen for realtime restaurant updates“ (Echtzeit-Restaurantaktualisierungen abrufen) und übertragen Sie ihn per Push in Ihr GitHub-Repository.
  2. Öffnen Sie in der Firebase Console die App Hosting-Seite und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  3. Wählen Sie in der Web-App 27ca5d1e8ed8adfe.png > Beispielrestaurants hinzufügen aus. Wenn die Snapshot-Funktion richtig implementiert ist, werden die Restaurants in Echtzeit ohne Seitenaktualisierung angezeigt.

8. Von Nutzern eingereichte Rezensionen aus der Web-App 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. Der Code aktualisiert auch das vorhandene Firestore-Dokument, das das Restaurant repräsentiert, mit aktualisierten Zahlen für die Anzahl der Bewertungen und die durchschnittliche 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 Nutzlast der Formularübermittlung abzurufen.

So verwenden Sie eine Next.js-Serveraktion, um das eingereichte Rezensionsformular zu verarbeiten:

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

Der Attributwert action verweist 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 Rezensionseinsendungen implementiert. Jetzt können Sie überprü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 ein Commit mit der Commit-Nachricht „Allow users to submit restaurant reviews“ (Nutzern erlauben, Restaurantrezensionen einzureichen) und übertragen Sie es per Push an Ihr GitHub-Repository.
  2. Öffnen Sie in der Firebase Console die App Hosting-Seite und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  3. Aktualisieren Sie die Web-App 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. Schreiben Sie eine Rezension.
  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 Restaurants, das Sie bewertet haben, 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, das Sie überprüfen möchten, um zu bestätigen, dass es wie erwartet eingefügt wurde.

Dokumente im Firestore-Emulator

9. Von Nutzern hochgeladene Dateien aus der Web-App speichern

In diesem Abschnitt fügen Sie Funktionen hinzu, mit denen Sie das Bild eines Restaurants 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 repräsentiert.

So speichern Sie von Nutzern hochgeladene Dateien aus der Web-App:

  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);
  setRestaurantDetails({ ...restaurantDetails, photo: imageURL });
}

An dieser Funktion sind keine Änderungen erforderlich. Das Verhalten der updateRestaurantImage()-Funktion wird in den folgenden Schritten implementiert.

  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 Funktion updateRestaurantImageReference() ist bereits für Sie implementiert. Diese Funktion aktualisiert ein vorhandenes Restaurantdokument in Cloud Firestore mit einer aktualisierten Bild-URL.

Funktion zum Hochladen von Bildern prüfen

So prüfen Sie, ob die Bilder wie erwartet hochgeladen werden:

  1. Erstellen Sie einen Commit mit der Commit-Nachricht „Allow users to change each restaurants' photo“ (Nutzern erlauben, das Foto jedes Restaurants zu ändern) und übertragen Sie ihn per Push in Ihr GitHub-Repository.
  2. Öffnen Sie in der Firebase Console die App Hosting-Seite und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  3. Prüfen Sie, ob Sie in der Web-App angemeldet sind, und wählen Sie ein Restaurant aus.
  4. Klicken Sie auf 7067eb41fea41ff0.png und laden Sie ein Bild aus Ihrem Dateisystem hoch. Ihr Bild verlässt Ihre lokale Umgebung und wird in Cloud Storage hochgeladen. Das Bild wird sofort nach dem Hochladen angezeigt.
  5. Rufen Sie Cloud Storage für Firebase auf.
  6. Rufen Sie den Ordner auf, der das Restaurant repräsentiert. Das von Ihnen hochgeladene Bild ist im Ordner vorhanden.

6cf3f9e2303c931c.png

10. Restaurantrezensionen mit generativer KI zusammenfassen

In diesem Abschnitt fügen Sie eine Funktion für die Zusammenfassung von Rezensionen hinzu, damit Nutzer schnell sehen können, was andere über ein Restaurant denken, 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. Rufen Sie Google AI Studio auf und klicken Sie auf „API-Schlüssel erstellen“.
  2. Wählen Sie in der Eingabeaufforderung „Google Cloud-Projekte durchsuchen“ Ihr Firebase-Projekt aus. Jedes Firebase-Projekt basiert auf einem Google Cloud-Projekt.
  3. App Hosting ist in Cloud Secret Manager eingebunden, damit Sie vertrauliche Werte wie API-Schlüssel sicher speichern können:
    1. Führen Sie in einem Terminal den Befehl zum Erstellen eines neuen Secrets aus:
    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 für die Produktion oder für lokale Tests bestimmt ist, wählen Sie „Production“ (Produktion) aus.
    3. Wenn Sie gefragt werden, ob Sie den Zugriff gewähren möchten, damit das Dienstkonto Ihres Back-Ends auf das Secret zugreifen kann, wählen Sie „Ja“ aus.
    4. Wenn Sie gefragt werden, ob das neue Secret zu apphosting.yaml hinzugefügt werden soll, geben Sie Y ein, um die Frage zu bejahen.

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

Komponente für die 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 reviewSeparator = "@";
      const prompt = `
        Based on the following restaurant reviews, 
        where each review is separated by a '${reviewSeparator}' character, 
        create a one-sentence summary of what people think of the restaurant. 
    
        Here are the reviews: ${reviews.map((review) => review.text).join(reviewSeparator)}
      `;
    
      try {
        if (!process.env.GEMINI_API_KEY) {
          // Make sure GEMINI_API_KEY environment variable is set:
          // https://firebase.google.com/docs/genkit/get-started
          throw new Error(
            'GEMINI_API_KEY not set. Set it with "firebase apphosting:secrets:set GEMINI_API_KEY"'
          );
        }
    
        // Configure a Genkit instance.
        const ai = genkit({
          plugins: [googleAI()],
          model: gemini20Flash, // set default model
        });
        const { text } = await ai.generate(prompt);
    
        return (
          <div className="restaurant__review_summary">
            <p>{text}</p>
            <p> Summarized with Gemini</p>
          </div>
        );
      } catch (e) {
        console.error(e);
        return <p>Error summarizing reviews.</p>;
      }
    }
    
  2. Erstellen Sie einen Commit mit der Commit-Nachricht „Use AI to summarize reviews“ (KI zum Zusammenfassen von Rezensionen verwenden) und übertragen Sie ihn per Push an Ihr GitHub-Repository.
  3. Öffnen Sie in der Firebase Console die App Hosting-Seite und warten Sie, bis die neue Bereitstellung abgeschlossen ist.
  4. Öffnen Sie die Seite eines Restaurants. Oben auf der Seite sehen Sie eine Zusammenfassung aller Rezensionen auf der Seite in einem Satz.
  5. Fügen Sie eine neue Rezension hinzu und aktualisieren Sie die Seite. Die Zusammenfassung der Änderung sollte angezeigt werden.

11. Fazit

Glückwunsch! Sie haben gelernt, wie Sie Firebase verwenden, um einer Next.js-App Funktionen hinzuzufügen. Insbesondere haben Sie Folgendes verwendet:

Weitere Informationen