Intégrer Firebase à une application Next.js

1. Avant de commencer

Dans cet atelier de programmation, vous allez apprendre à intégrer Firebase à une application Web Next.js appelée Friendly Eats, qui est un site Web d'avis sur les restaurants.

Application Web Friendly Eats

L'application Web terminée offre des fonctionnalités utiles qui montrent comment Firebase peut vous aider à créer des applications Next.js. Voici quelques exemples de ces fonctionnalités :

  • Compilation et déploiement automatiques : cet atelier de programmation utilise Firebase App Hosting pour compiler et déployer automatiquement votre code Next.js chaque fois que vous envoyez des modifications vers une branche configurée.
  • Connexion et déconnexion : l'application Web terminée vous permet de vous connecter avec Google et de vous déconnecter. La connexion et la persistance des utilisateurs sont entièrement gérées par Firebase Authentication.
  • Images : l'application Web terminée permet aux utilisateurs connectés d'importer des images de restaurants. Les composants d'image sont stockés dans Cloud Storage pour Firebase. Le SDK Firebase JavaScript fournit une URL publique pour les images importées. Cette URL publique est ensuite stockée dans le document du restaurant concerné dans Cloud Firestore.
  • Avis : l'application Web terminée permet aux utilisateurs connectés de publier des avis sur les restaurants. Ces avis se composent d'une note et d'un message textuel. Les informations sur les avis sont stockées dans Cloud Firestore.
  • Filtres : l'application Web terminée permet aux utilisateurs connectés de filtrer la liste des restaurants en fonction de la catégorie, de l'emplacement et du prix. Vous pouvez également personnaliser la méthode de tri utilisée. Les données sont accessibles depuis Cloud Firestore, et les requêtes Firestore sont appliquées en fonction des filtres utilisés.

Prérequis

  • Un compte GitHub
  • Connaissances de Next.js et de JavaScript

Points abordés

  • Comment utiliser Firebase avec le routeur d'application Next.js et le rendu côté serveur.
  • Persister des images dans Cloud Storage pour Firebase
  • Comment lire et écrire des données dans une base de données Cloud Firestore.
  • Découvrez comment utiliser la connexion avec Google avec le SDK Firebase JavaScript.

Prérequis

  • Git
  • Une version stable récente de Node.js
  • Un navigateur de votre choix, tel que Google Chrome
  • Un environnement de développement avec un éditeur de code et un terminal
  • Un compte Google pour créer et gérer votre projet Firebase
  • La possibilité de passer à la formule Blaze pour votre projet Firebase

2. Configurer votre environnement de développement et votre dépôt GitHub

Cet atelier de programmation fournit le code de base de l'application et s'appuie sur la CLI Firebase.

Créer un dépôt GitHub

Le code source de l'atelier de programmation est disponible à l'adresse https://github.com/firebase/friendlyeats-web. Le dépôt contient des exemples de projets adaptés à différentes plates-formes. Cependant, cet atelier de programmation n'utilise que le répertoire nextjs-start. Notez les répertoires suivants :

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

Copiez le dossier nextjs-start dans votre propre dépôt :

  1. À l'aide d'un terminal, créez un dossier sur votre ordinateur et accédez-y :
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. Utilisez le package npm giget pour n'extraire que le dossier nextjs-start :
    npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
    
  3. Suivez les modifications en local avec git :
    git init
    
    git add .
    
    git commit -m "codelab starting point"
    
    git branch -M main
    
  4. Créez un dépôt GitHub : https://github.com/new. Donnez-lui le nom de votre choix.
  5. Copiez la nouvelle URL que GitHub a créée pour vous. Voici quelques exemples :
    • https://github.com/<USER_NAME>/<REPOSITORY_NAME>.git ou
    • git@github.com:<USER_NAME>/<REPOSITORY_NAME>.git
  6. Transférez les modifications locales vers votre nouveau dépôt GitHub en exécutant la commande suivante. Remplacez l'espace réservé <REPOSITORY_URL> par l'URL réelle de votre dépôt.
    git remote add origin <REPOSITORY_URL>
    
    git push -u origin main
    
  7. Le code de démarrage devrait maintenant s'afficher dans votre dépôt GitHub.

Installer ou mettre à jour la CLI Firebase

Exécutez la commande suivante pour vérifier que la CLI Firebase est installée et qu'il s'agit de la version 14.1.0 ou ultérieure :

firebase --version

Si vous voyez une version antérieure ou si la CLI Firebase n'est pas installée, exécutez la commande d'installation :

npm install -g firebase-tools@latest

Si vous ne parvenez pas à installer la CLI Firebase en raison d'erreurs d'autorisation, consultez la documentation npm ou utilisez une autre option d'installation.

Se connecter à Firebase

  1. Exécutez la commande suivante pour vous connecter à la CLI Firebase :
    firebase login
    
  2. Saisissez Y ou N selon que vous souhaitez ou non que Firebase collecte des données.
  3. Dans votre navigateur, sélectionnez votre compte Google, puis cliquez sur Autoriser.

3. Configurer votre projet Firebase

Dans cette section, vous allez configurer un projet Firebase et y associer une application Web Firebase. Vous configurerez également les services Firebase utilisés par l'exemple d'application Web.

Créer un projet Firebase

  1. Connectez-vous à la console Firebase avec le même compte Google que celui utilisé à l'étape précédente.
  2. Cliquez sur le bouton pour créer un projet, puis saisissez un nom de projet (par exemple, FriendlyEats Codelab).
  3. Cliquez sur Continuer.
  4. Si vous y êtes invité, lisez et acceptez les Conditions d'utilisation de Firebase, puis cliquez sur Continuer.
  5. (Facultatif) Activez l'assistance IA dans la console Firebase (appelée "Gemini dans Firebase").
  6. Pour cet atelier de programmation, vous n'avez pas besoin de Google Analytics. Désactivez donc l'option Google Analytics.
  7. Cliquez sur Créer un projet, attendez que votre projet soit provisionné, puis cliquez sur Continuer.

Passer à un forfait Firebase supérieur

Pour utiliser Firebase App Hosting et Cloud Storage pour Firebase, votre projet Firebase doit être associé à un compte de facturation Cloud et être soumis au forfait Blaze avec paiement à l'usage.

  • Un compte de facturation Cloud nécessite un mode de paiement, comme une carte de crédit.
  • Si vous débutez avec Firebase et Google Cloud, vérifiez si vous êtes éligible à un crédit de 300$et à un compte de facturation Cloud pour l'essai sans frais.
  • Si vous effectuez cet atelier de programmation dans le cadre d'un événement, demandez à l'organisateur si des crédits Cloud sont disponibles.

Pour passer à la formule Blaze, procédez comme suit :

  1. Dans la console Firebase, sélectionnez Passer à une formule supérieure.
  2. Sélectionnez le forfait Blaze. Suivez les instructions à l'écran pour associer un compte de facturation Cloud à votre projet.
    Si vous avez dû créer un compte de facturation Cloud lors de cette mise à niveau, vous devrez peut-être revenir au processus de mise à niveau dans la console Firebase pour la finaliser.

Ajouter une application Web à votre projet Firebase

  1. Accédez à la vue d'ensemble du projet dans votre projet Firebase, puis cliquez sur e41f2efdd9539c31.png Web.

    Si des applications sont déjà enregistrées dans votre projet, cliquez sur Ajouter une application pour afficher l'icône Web.
  2. Dans la zone de texte Pseudo de l'application, saisissez un pseudo facile à retenir, par exemple My Next.js app.
  3. Ne cochez pas la case Configurer également Firebase Hosting pour cette application.
  4. Cliquez sur Enregistrer l'application > Accéder à la console.

Configurer les services Firebase dans la console Firebase

Configurer l'authentification

  1. Dans la console Firebase, accédez à Authentification.
  2. Cliquez sur Commencer.
  3. Dans la colonne Fournisseurs supplémentaires, cliquez sur Google > Activer.
  4. Dans la zone de texte Nom public du projet, saisissez un nom facile à retenir, par exemple My Next.js app.
  5. Dans la liste déroulante Adresse e-mail d'assistance pour le projet, sélectionnez votre adresse e-mail.
  6. Cliquez sur Enregistrer.

Configurer Cloud Firestore

  1. Dans le panneau de gauche de la console Firebase, développez Créer, puis sélectionnez Base de données Firestore.
  2. Cliquez sur Créer une base de données.
  3. Laissez le champ ID de la base de données défini sur (default).
  4. Sélectionnez un emplacement pour votre base de données, puis cliquez sur Suivant.
    Pour une application réelle, choisissez un emplacement proche de vos utilisateurs.
  5. Cliquez sur Démarrer en mode test. Lisez la clause de non-responsabilité concernant les règles de sécurité.
    Dans cet atelier de programmation, vous ajouterez des règles de sécurité pour protéger vos données. Ne distribuez ni n'exposez publiquement une application sans ajouter de règles de sécurité pour votre base de données.
  6. Cliquez sur Créer.

Configurer Cloud Storage for Firebase

  1. Dans le panneau de gauche de la console Firebase, développez Créer, puis sélectionnez Stockage.
  2. Cliquez sur Commencer.
  3. Sélectionnez un emplacement pour votre bucket Storage par défaut.
    Les buckets situés dans les régions US-WEST1, US-CENTRAL1 et US-EAST1 peuvent profiter du niveau"Toujours sans frais" pour Google Cloud Storage. Les buckets situés dans toutes les autres régions sont soumis aux tarifs et à l'utilisation de Google Cloud Storage.
  4. Cliquez sur Démarrer en mode test. Lisez la clause de non-responsabilité concernant les règles de sécurité.
    Dans une étape ultérieure de cet atelier de programmation, vous ajouterez des règles de sécurité pour protéger vos données. Ne distribuez ni n'exposez publiquement une application sans ajouter de règles de sécurité pour votre bucket Storage.
  5. Cliquez sur Créer.

Déployer les règles de sécurité

Le code comporte déjà des ensembles de règles de sécurité pour Firestore et pour Cloud Storage for Firebase. Une fois les règles de sécurité déployées, les données de votre base de données et de votre bucket sont mieux protégées contre les utilisations abusives.

  1. Dans votre terminal, configurez la CLI pour qu'elle utilise le projet Firebase que vous avez créé précédemment :
    firebase use --add
    
    Lorsque vous êtes invité à saisir un alias, saisissez friendlyeats-codelab.
  2. Pour déployer ces règles de sécurité (ainsi que les index qui seront nécessaires ultérieurement), exécutez cette commande dans votre terminal :
    firebase deploy --only firestore,storage
    
  3. Si le message "Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?" s'affiche, appuyez sur Enter pour sélectionner Oui.

4. Examiner le codebase de démarrage

Dans cette section, vous allez examiner quelques zones du code de base de l'application auxquelles vous ajouterez des fonctionnalités dans cet atelier de programmation.

Structure des dossiers et des fichiers

Le tableau suivant présente la structure des dossiers et des fichiers de l'application :

Dossiers et fichiers

Description

src/components

Composants React pour les filtres, les en-têtes, les informations sur les restaurants et les avis

src/lib

Fonctions utilitaires qui ne sont pas nécessairement liées à React ou Next.js

src/lib/firebase

Code et configuration Firebase

public

Éléments statiques de l'application Web, comme les icônes

src/app

Routage avec l'outil de routage d'application Next.js

package.json et package-lock.json

Dépendances du projet avec npm

next.config.js

Configuration spécifique à Next.js (les actions de serveur sont activées)

jsconfig.json

Configuration du service linguistique JavaScript

Composants serveur et client

L'application est une application Web Next.js qui utilise le routeur d'application. Le rendu côté serveur est utilisé dans toute l'application. Par exemple, le fichier src/app/page.js est un composant serveur responsable de la page principale. Le fichier src/components/RestaurantListings.jsx est un composant client indiqué par la directive "use client" au début du fichier.

Instructions d'importation

Vous pouvez remarquer des instructions d'importation comme celles-ci :

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

L'application utilise le symbole @ pour éviter les chemins d'importation relatifs maladroits, ce qui est possible grâce aux alias de chemin d'accès.

API spécifiques à Firebase

Tout le code de l'API Firebase est encapsulé dans le répertoire src/lib/firebase. Les composants React individuels importent ensuite les fonctions encapsulées à partir du répertoire src/lib/firebase, au lieu d'importer directement les fonctions Firebase.

Données fictives

Les données de restaurant et d'avis fictives sont contenues dans le fichier src/lib/randomData.js. Les données de ce fichier sont assemblées dans le code du fichier src/lib/fakeRestaurants.js.

5. Créer un backend App Hosting

Dans cette section, vous allez configurer un backend App Hosting pour surveiller une branche de votre dépôt Git.

À la fin de cette section, vous disposerez d'un backend App Hosting connecté à votre dépôt dans GitHub. Il reconstruira et déploiera automatiquement une nouvelle version de votre application chaque fois que vous enverrez un nouveau commit à votre branche main.

Créer un backend

  1. Accédez à la page App Hosting dans la console Firebase :

État zéro de la console App Hosting, avec un bouton &quot;Get Started&quot; (Commencer)

  1. Cliquez sur "Commencer" pour lancer le flux de création du backend. Configurez votre backend comme suit :
  2. Sélectionnez une région. Pour une application réelle, vous devez choisir la région la plus proche de vos utilisateurs.
  3. Suivez les instructions de l'étape "Importer un dépôt GitHub" pour connecter le dépôt GitHub que vous avez créé précédemment.
  4. Définissez les paramètres de déploiement :
    1. Conserver le répertoire racine sous le nom /
    2. Définissez la branche "live" sur main.
    3. Activer les déploiements automatiques
  5. Nommez votre backend friendlyeats-codelab.
  6. Dans "Associer une application Web Firebase", cliquez sur "Créer une application Web Firebase".
  7. Cliquez sur "Terminer et déployer". Au bout de quelques instants, vous serez redirigé vers une nouvelle page où vous pourrez consulter l'état de votre nouveau backend App Hosting.
  8. Une fois le déploiement terminé, cliquez sur votre domaine sans frais sous "Domaines". La propagation DNS peut prendre quelques minutes.
  9. Petit problème… Lorsque vous chargez la page, un message d'erreur s'affiche : "Erreur d'application : une exception côté serveur s'est produite (consultez les journaux du serveur pour en savoir plus)."
  10. Dans la console Firebase, consultez l'onglet "Journaux" du backend App Hosting. Un journal "Error: not implemented " (Erreur : non implémenté) s'affiche. Nous corrigerons ce problème à l'étape suivante, lorsque nous ajouterons l'authentification.

Vous avez déployé l'application Web initiale. Chaque fois que vous envoyez un nouveau commit vers la branche main de votre dépôt GitHub, une nouvelle compilation et un nouveau déploiement démarrent dans la console Firebase. Votre site est automatiquement mis à jour une fois le déploiement terminé.

6. Ajouter l'authentification à l'application Web

Dans cette section, vous allez ajouter l'authentification à l'application Web pour pouvoir vous y connecter.

Ajouter un domaine autorisé

Firebase Authentication n'accepte que les demandes de connexion provenant des domaines que vous autorisez. Ici, nous allons ajouter le domaine du backend App Hosting à la liste des domaines approuvés dans votre projet.

  1. Copiez le domaine de votre backend App Hosting depuis la page "Présentation " d'App Hosting.
  2. Accédez à l'onglet Auth Settings (Paramètres d'authentification) et sélectionnez Authorized Domains (Domaines autorisés).
  3. Cliquez sur le bouton Ajouter un domaine.
  4. Saisissez le domaine de votre backend App Hosting.
  5. Cliquez sur Ajouter.

Implémenter les fonctions de connexion et de déconnexion

  1. Dans le fichier src/lib/firebase/auth.js, remplacez les fonctions onAuthStateChanged, onIdTokenChanged, signInWithGoogle et signOut par le code suivant :
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);
  }
}

Ce code utilise les API Firebase suivantes :

API Firebase

Description

auth.onAuthStateChanged

Ajoute un observateur pour les modifications apportées à l'état de connexion de l'utilisateur.

auth.onIdTokenChanged

Ajoute un observateur pour les modifications apportées au jeton d'ID de l'utilisateur.

GoogleAuthProvider

Crée une instance de fournisseur d'authentification Google.

signInWithPopup

Lance un flux d'authentification basé sur une boîte de dialogue.

auth.signOut

Déconnecte l'utilisateur.

Dans le fichier src/components/Header.jsx, le code appelle déjà les fonctions signInWithGoogle et signOut.

Envoyer l'état de l'authentification au serveur

Pour transmettre l'état d'authentification au serveur, nous allons utiliser des cookies. Chaque fois que l'état de l'authentification change dans le client, nous mettons à jour le cookie __session.

Dans src/components/Header.jsx, remplacez la fonction useUserSession par le code suivant :

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

Lire l'état de l'authentification sur le serveur

Nous allons utiliser FirebaseServerApp pour refléter l'état d'authentification du client sur le serveur.

Ouvrez src/lib/firebase/serverApp.js et remplacez la fonction 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 };
}

Vérifier les modifications

La mise en page racine du fichier src/app/layout.js affiche l'en-tête et transmet l'utilisateur, le cas échéant, en tant que propriété.

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

Cela signifie que le composant <Header> affiche les données utilisateur, si elles sont disponibles, pendant l'exécution du serveur. Si des mises à jour de l'authentification ont lieu pendant le cycle de vie de la page après le chargement initial, le gestionnaire onAuthStateChanged les gère.

Il est maintenant temps de déployer une nouvelle version et de vérifier ce que vous avez créé.

  1. Créez un commit avec le message "Add authentication" (Ajouter l'authentification) et transférez-le vers votre dépôt GitHub.
  2. Ouvrez la page App Hosting dans la console Firebase et attendez que votre nouveau déploiement soit terminé.
  3. Vérifiez le nouveau comportement d'authentification :
    1. Dans votre navigateur, actualisez l'application Web. Votre nom à afficher apparaît dans l'en-tête.
    2. Déconnectez-vous, puis reconnectez-vous. Vous pouvez répéter cette étape avec différents utilisateurs.
    3. Facultatif : Effectuez un clic droit sur l'application Web, sélectionnez Afficher le code source de la page, puis recherchez le nom à afficher. Il apparaît dans le code source HTML brut renvoyé par le serveur.

7. Afficher les informations sur un restaurant

L'application Web inclut des données fictives pour les restaurants et les avis.

Ajouter un ou plusieurs restaurants

Pour insérer des données de restaurant fictives dans votre base de données Cloud Firestore locale, procédez comme suit :

  1. Connectez-vous à l'application Web si ce n'est pas déjà fait. Ensuite, sélectionnez 2cf67d488d8e6332.png > Ajouter des exemples de restaurants.
  2. Dans la console Firebase, sur la page Firestore Database, sélectionnez restaurants. Vous voyez les documents de premier niveau dans la collection de restaurants, chacun représentant un restaurant.
  3. Cliquez sur quelques documents pour explorer les propriétés d'un document de restaurant.

Afficher la liste des restaurants

Votre base de données Cloud Firestore contient désormais des restaurants que l'application Web Next.js peut afficher.

Pour définir le code d'extraction des données, procédez comme suit :

  1. Dans le fichier src/app/page.js, recherchez le composant serveur <Home /> et examinez l'appel de la fonction getRestaurants, qui récupère une liste de restaurants lors de l'exécution du serveur. Vous allez implémenter la fonction getRestaurants au cours des étapes suivantes.
  2. Dans le fichier src/lib/firebase/firestore.js, remplacez les fonctions applyQueryFilters et getRestaurants par le code suivant :
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. Créez un commit avec le message "Read the list of restaurants from Firestore" (Lire la liste des restaurants depuis Firestore) et envoyez-le à votre dépôt GitHub.
  2. Ouvrez la page App Hosting dans la console Firebase et attendez que votre nouveau déploiement soit terminé.
  3. Dans l'application Web, actualisez la page. Les images des restaurants s'affichent sous forme de mosaïques sur la page.

Vérifier que les fiches de restaurant se chargent lors de l'exécution du serveur

Avec le framework Next.js, il n'est pas toujours évident de savoir quand les données sont chargées au moment de l'exécution du serveur ou du client.

Pour vérifier que les fiches de restaurants se chargent lors de l'exécution du serveur, procédez comme suit :

  1. Dans l'application Web, ouvrez les outils pour les développeurs et désactivez JavaScript.

Désactiver JavaScript dans les outils de développement

  1. Actualisez l'application Web. Les fiches d'établissement se chargent toujours. Les informations sur le restaurant sont renvoyées dans la réponse du serveur. Lorsque JavaScript est activé, les informations sur le restaurant sont hydratées via le code JavaScript côté client.
  2. Dans les outils de développement, réactivez JavaScript.

Écouter les mises à jour de restaurants avec les écouteurs d'instantanés Cloud Firestore

Dans la section précédente, vous avez vu comment l'ensemble initial de restaurants était chargé à partir du fichier src/app/page.js. Le fichier src/app/page.js est un composant serveur qui est affiché sur le serveur, y compris le code de récupération des données Firebase.

Le fichier src/components/RestaurantListings.jsx est un composant client qui peut être configuré pour hydrater le balisage rendu par le serveur.

Pour configurer le fichier src/components/RestaurantListings.jsx afin d'hydrater le balisage rendu côté serveur, procédez comme suit :

  1. Dans le fichier src/components/RestaurantListings.jsx, examinez le code suivant, qui est déjà écrit pour vous :
useEffect(() => {
    return getRestaurantsSnapshot((data) => {
      setRestaurants(data);
    }, filters);
  }, [filters]);

Ce code appelle la fonction getRestaurantsSnapshot(), qui est semblable à la fonction getRestaurants() que vous avez implémentée à l'étape précédente. Toutefois, cette fonction d'instantané fournit un mécanisme de rappel afin que le rappel soit invoqué chaque fois qu'une modification est apportée à la collection du restaurant.

  1. Dans le fichier src/lib/firebase/firestore.js, remplacez la fonction getRestaurantsSnapshot() par le code suivant :
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);
  });
}

Les modifications apportées sur la page Base de données Firestore sont désormais reflétées en temps réel dans l'application Web.

  1. Créez un commit avec le message "Listen for realtime restaurant updates" (Écouter les mises à jour en temps réel des restaurants) et déployez-le dans votre dépôt GitHub.
  2. Ouvrez la page App Hosting dans la console Firebase et attendez que votre nouveau déploiement soit terminé.
  3. Dans l'application Web, sélectionnez 27ca5d1e8ed8adfe.png > Ajouter des restaurants exemples. Si votre fonction d'instantané est correctement implémentée, les restaurants s'affichent en temps réel sans actualisation de la page.

8. Enregistrer les avis envoyés par les utilisateurs depuis l'application Web

  1. Dans le fichier src/lib/firebase/firestore.js, remplacez la fonction updateWithRating() par le code suivant :
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()),
  });
};

Ce code insère un nouveau document Firestore représentant le nouvel avis. Le code met également à jour le document Firestore existant qui représente le restaurant avec les chiffres actualisés du nombre de notes et de la note moyenne calculée.

  1. Remplacez la fonction addReviewToRestaurant() par le code suivant :
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;
	}
}

Implémenter une action de serveur Next.js

Une action de serveur Next.js fournit une API pratique pour accéder aux données de formulaire, telles que data.get("text") pour obtenir la valeur du texte à partir de la charge utile de l'envoi du formulaire.

Pour utiliser une action de serveur Next.js afin de traiter l'envoi du formulaire d'avis, procédez comme suit :

  1. Dans le fichier src/components/ReviewDialog.jsx, recherchez l'attribut action dans l'élément <form>.
<form action={handleReviewFormSubmission}>

La valeur de l'attribut action fait référence à une fonction que vous implémenterez à l'étape suivante.

  1. Dans le fichier src/app/actions.js, remplacez la fonction handleReviewFormSubmission() par le code suivant :
// 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"),
        });
}

Ajouter des avis sur un restaurant

Vous avez implémenté la prise en charge des envois d'avis. Vous pouvez maintenant vérifier que vos avis sont correctement insérés dans Cloud Firestore.

Pour ajouter un avis et vérifier qu'il est inséré dans Cloud Firestore, procédez comme suit :

  1. Créez un commit avec le message "Allow users to submit restaurant reviews" (Autoriser les utilisateurs à envoyer des avis sur les restaurants) et envoyez-le à votre dépôt GitHub.
  2. Ouvrez la page App Hosting dans la console Firebase et attendez que votre nouveau déploiement soit terminé.
  3. Actualisez l'application Web, puis sélectionnez un restaurant sur la page d'accueil.
  4. Sur la page du restaurant, cliquez sur 3e19beef78bb0d0e.png.
  5. Sélectionnez une note.
  6. Rédigez un avis.
  7. Cliquez sur Envoyer. Votre avis s'affiche en haut de la liste des avis.
  8. Dans Cloud Firestore, recherchez le document du restaurant que vous avez évalué dans le volet Ajouter un document, puis sélectionnez-le.
  9. Dans le volet Commencer la collecte, sélectionnez Notes.
  10. Dans le volet Ajouter un document, recherchez le document à examiner pour vérifier qu'il a été inséré comme prévu.

Documents dans l&#39;émulateur Firestore

9. Enregistrer les fichiers importés par les utilisateurs depuis l'application Web

Dans cette section, vous allez ajouter une fonctionnalité qui vous permettra de remplacer l'image associée à un restaurant lorsque vous êtes connecté. Vous importez l'image dans Firebase Storage et mettez à jour l'URL de l'image dans le document Cloud Firestore qui représente le restaurant.

Pour enregistrer les fichiers importés par les utilisateurs depuis l'application Web, procédez comme suit :

  1. Dans le fichier src/components/Restaurant.jsx, observez le code qui s'exécute lorsque l'utilisateur importe un fichier :
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 });
}

Aucune modification n'est nécessaire pour cette fonction, mais vous implémenterez le comportement de la fonction updateRestaurantImage() dans les étapes suivantes.

  1. Dans le fichier src/lib/firebase/storage.js, remplacez les fonctions updateRestaurantImage() et uploadImage() par le code suivant :
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);
}

La fonction updateRestaurantImageReference() est déjà implémentée pour vous. Cette fonction met à jour un document de restaurant existant dans Cloud Firestore avec une URL d'image modifiée.

Vérifier la fonctionnalité d'importation d'images

Pour vérifier que les images sont importées comme prévu, procédez comme suit :

  1. Créez un commit avec le message "Allow users to change each restaurants' photo" (Autoriser les utilisateurs à modifier la photo de chaque restaurant) et déployez-le dans votre dépôt GitHub.
  2. Ouvrez la page App Hosting dans la console Firebase et attendez que votre nouveau déploiement soit terminé.
  3. Dans l'application Web, vérifiez que vous êtes connecté et sélectionnez un restaurant.
  4. Cliquez sur 7067eb41fea41ff0.png et importez une image depuis votre système de fichiers. Votre image quitte votre environnement local et est importée dans Cloud Storage. L'image apparaît immédiatement après que vous l'avez importée.
  5. Accédez à Cloud Storage pour Firebase.
  6. Accédez au dossier qui représente le restaurant. L'image que vous avez importée se trouve dans le dossier.

6cf3f9e2303c931c.png

10. Résumer les avis sur les restaurants avec l'IA générative

Dans cette section, vous allez ajouter une fonctionnalité de résumé des avis afin qu'un utilisateur puisse rapidement comprendre ce que tout le monde pense d'un restaurant sans avoir à lire tous les avis.

Stocker une clé API Gemini dans Cloud Secret Manager

  1. Pour utiliser l'API Gemini, vous avez besoin d'une clé API. Accédez à Google AI Studio, puis cliquez sur "Create API Key" (Créer une clé API).
  2. Dans le champ de saisie "Rechercher des projets Google Cloud", sélectionnez votre projet Firebase. Chaque projet Firebase s'appuie sur un projet Google Cloud.
  3. App Hosting s'intègre à Cloud Secret Manager pour vous permettre de stocker des valeurs sensibles telles que des clés API de manière sécurisée :
    1. Dans un terminal, exécutez la commande pour créer un secret :
    firebase apphosting:secrets:set GEMINI_API_KEY
    
    1. Lorsque vous êtes invité à saisir la valeur secrète, copiez et collez votre clé API Gemini depuis Google AI Studio.
    2. Lorsque vous êtes invité à indiquer si le nouveau secret est destiné à la production ou aux tests locaux, sélectionnez "Production".
    3. Lorsque vous êtes invité à accorder l'accès pour que le compte de service de votre backend puisse accéder au secret, sélectionnez "Oui".
    4. Lorsque vous êtes invité à ajouter le nouveau secret à apphosting.yaml, saisissez Y pour accepter.

Votre clé API Gemini est désormais stockée de manière sécurisée dans Cloud Secret Manager et est accessible à votre backend App Hosting.

Implémenter le composant de résumé des avis

  1. Dans src/components/Reviews/ReviewSummary.jsx, remplacez la fonction GeminiSummary par le code suivant :
    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. Créez un commit avec le message "Utiliser l'IA pour résumer les avis" et envoyez-le à votre dépôt GitHub.
  3. Ouvrez la page App Hosting dans la console Firebase et attendez que votre nouveau déploiement soit terminé.
  4. Ouvrez la page d'un restaurant. En haut de la page, vous devriez voir un résumé d'une phrase de tous les avis.
  5. Ajoutez un nouvel avis et actualisez la page. La modification du résumé devrait s'afficher.

11. Conclusion

Félicitations ! Vous avez appris à utiliser Firebase pour ajouter des fonctionnalités à une application Next.js. Plus précisément, vous avez utilisé les éléments suivants :

En savoir plus