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. Ces fonctionnalités incluent les suivantes:

  • 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 un push 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 éléments Image sont stockés dans Cloud Storage for Firebase. Le SDK JavaScript Firebase 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, qui 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 JavaScript

Points abordés

  • Utiliser Firebase avec le routeur d'application Next.js et le rendu côté serveur
  • Comment conserver des images dans Cloud Storage for Firebase
  • Lire et écrire des données dans une base de données Cloud Firestore
  • Utiliser la connexion avec Google avec le SDK JavaScript Firebase

Prérequis

  • Git
  • Une version stable récente de Node.js
  • Un navigateur de votre choix, comme 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 votre projet Firebase au forfait Blaze

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

Cet atelier de programmation fournit le code de démarrage 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 au nouveau répertoire:
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. Utilisez le package npm giget pour ne récupérer que le dossier nextjs-start:
    npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
    
  3. Suivez les modifications localement avec git:
    git init
    
    git commit -a -m "codelab starting point"
    
    git branch -M main
    
  4. Créez un dépôt GitHub: https://github.com/new. Vous pouvez lui donner le nom de votre choix.
    1. GitHub vous fournira une nouvelle URL de dépôt qui ressemble à https://github.com//.git ou git@github.com:/.git. Copiez cette URL.
  5. Transférez les modifications locales vers votre nouveau dépôt GitHub. Exécutez la commande suivante en remplaçant l'espace réservé par l'URL de votre dépôt.
    git remote add origin <your-repository-url>
    
    git push -u origin main
    
  6. 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'elle est de version 13.9.0 ou ultérieure:

firebase --version

Si une version antérieure s'affiche 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. Selon que vous souhaitez que Firebase collecte des données, saisissez Y ou N.
  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 allez également configurer les services Firebase utilisés par l'application Web exemple.

Créer un projet Firebase

  1. Dans la console Firebase, cliquez sur Ajouter un projet.
  2. Dans la zone de texte Enter your project name (Saisissez le nom de votre projet), saisissez FriendlyEats Codelab (ou le nom de votre choix), puis cliquez sur Continue (Continuer).
  3. Dans la fenêtre modale Confirm Firebase billing plan (Confirmer le mode de facturation Firebase), vérifiez que le forfait est Blaze, puis cliquez sur Confirm plan (Confirmer le mode de facturation).
  4. Pour cet atelier de programmation, vous n'avez pas besoin de Google Analytics. Désactivez donc l'option Activer Google Analytics pour ce projet.
  5. Cliquez sur Create Project (Créer un projet).
  6. Attendez que votre projet soit provisionné, puis cliquez sur Continuer.
  7. Dans votre projet Firebase, accédez à Paramètres du projet. Notez l'ID de votre projet, car vous en aurez besoin plus tard. Cet identifiant unique permet d'identifier votre projet (par exemple, dans la CLI Firebase).

Passer à un forfait Firebase supérieur

Pour utiliser Firebase App Hosting et Cloud Storage for Firebase, votre projet Firebase doit être associé au forfait Blaze avec paiement à l'usage, ce qui signifie qu'il est associé à un compte de facturation Cloud.

  • 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 pouvez bénéficier d'un crédit de 300$et d'un compte de facturation Cloud en essai sans frais.
  • Si vous suivez cet atelier de programmation dans le cadre d'un événement, demandez à l'organisateur s'il existe des crédits Cloud disponibles.

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

  1. Dans la console Firebase, sélectionnez l'option Mettre à niveau votre forfait.
  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 dans le cadre de cette migration, vous devrez peut-être revenir au flux de migration dans la console Firebase pour la terminer.

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 App nickname (Pseudo de l'application), saisissez un pseudo d'application 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 > Suivant > Suivant > 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 Build (Compilation), puis sélectionnez Firestore database (Base de données Firestore).
  2. Cliquez sur Créer une base de données.
  3. Laissez le champ Database ID (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é.
    Plus tard dans cet atelier de programmation, vous ajouterez des règles de sécurité pour sécuriser vos données. Ne distribuez pas ni n'exposez pas publiquement une application sans ajouter de règles de sécurité à 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 Build (Compilation), puis sélectionnez Storage (Stockage).
  2. Cliquez sur Commencer.
  3. Sélectionnez un emplacement pour votre bucket Storage par défaut.
    Les buckets dans 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 zones géographiques 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é.
    Plus tard dans cet atelier de programmation, vous ajouterez des règles de sécurité pour sécuriser vos données. Ne distribuez pas ni n'exposez pas publiquement une application sans ajouter de règles de sécurité à votre bucket Storage.
  5. Cliquez sur Créer.

4. Examiner le code de démarrage

Dans cette section, vous allez examiner quelques éléments du code de démarrage de l'application auxquels vous allez ajouter 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 spécifiques

public

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

src/app

Routage avec le routeur d'application Next.js

src/app/restaurant

Un gestionnaire de routage d'API

package.json et package-lock.json

Dépendances du projet avec npm

next.config.js

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

jsconfig.json

Configuration du service de langage JavaScript

Composants du serveur et du client

Il s'agit d'une application Web Next.js qui utilise le routeur d'application. L'affichage côté serveur est utilisé dans l'ensemble de 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 telles que les suivantes:

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

L'application utilise le symbole @ pour éviter les chemins d'importation relatifs peu pratiques et est rendue possible par les 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, plutôt que d'importer directement les fonctions Firebase.

Données fictives

Les données fictives sur les restaurants et les avis 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 aurez un backend App Hosting connecté à votre dépôt GitHub. Il recompilera et déploiera automatiquement une nouvelle version de votre application chaque fois que vous enverrez un nouveau commit dans votre branche main.

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

Le code contient déjà des ensembles de règles de sécurité pour Firestore et 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é, exécutez la commande suivante dans votre terminal:
    firebase deploy --only firestore:rules,storage
    
  3. Si vous êtes invité à répondre à la question: "Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?", appuyez sur Enter pour sélectionner Oui.

Ajouter votre configuration Firebase au code de votre application Web

  1. Dans la console Firebase, accédez à vos Paramètres du projet.
  2. Dans le volet Configuration et configuration du SDK, cliquez sur "Ajouter une application", puis sur l'icône des crochets de code  pour enregistrer une nouvelle application Web.
  3. À la fin du flux de création d'une application Web, copiez la variable firebaseConfig, ainsi que ses propriétés et leurs valeurs.
  4. Ouvrez le fichier apphosting.yaml dans votre éditeur de code, puis remplacez les valeurs des variables d'environnement par les valeurs de configuration de la console Firebase.
  5. Dans le fichier, remplacez les propriétés existantes par celles que vous avez copiées.
  6. Enregistrez le fichier.

Créer un backend

  1. Accédez à la page Hébergement d'applications dans la console Firebase:

État initial de la console App Hosting, avec un bouton &quot;Commencer&quot;

  1. Cliquez sur "Commencer" pour lancer le flux de création du backend. Configurez votre backend comme suit:
  2. Suivez les instructions de la première étape pour connecter le dépôt GitHub que vous avez créé précédemment.
  3. Définissez les paramètres de déploiement:
    1. Laisser le répertoire racine tel quel (/)
    2. Définissez la branche en direct sur main.
    3. Activer les déploiements automatiques
  4. Nommez votre backend friendlyeats-codelab.
  5. Dans "Créer ou associer une application Web Firebase", sélectionnez l'application Web que vous avez configurée précédemment dans le menu déroulant "Sélectionner une application Web Firebase existante".
  6. Cliquez sur "Terminer et déployer". Après un moment, vous serez redirigé vers une nouvelle page où vous pourrez consulter l'état de votre nouveau backend d'hébergement d'applications.
  7. Une fois le déploiement terminé, cliquez sur votre domaine sans frais sous "domaines". La propagation du DNS peut prendre quelques minutes.

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 commencent dans la console Firebase. Votre site est automatiquement mis à jour une fois le déploiement terminé.

6. Ajouter une authentification à l'application Web

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

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

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

Ce code utilise les API Firebase suivantes:

API Firebase

Description

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.

  1. Créez un commit avec le message "Ajout de l'authentification Google" et transférez-le vers votre dépôt GitHub. 1. Ouvrez la page Hébergement d'applications dans la console Firebase, puis attendez que le déploiement soit terminé.
  2. Dans l'application Web, actualisez la page, puis cliquez sur Se connecter avec Google. L'application Web ne s'actualise pas. Il est donc difficile de savoir si la connexion a réussi.

Envoyer l'état d'authentification au serveur

Pour transmettre l'état d'authentification au serveur, nous allons utiliser un service worker. Remplacez les fonctions fetchWithFirebaseHeaders et getAuthIdToken par le code suivant:

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

Lire l'état d'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 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 };
}

S'abonner aux modifications liées à l'authentification

Pour vous abonner aux modifications d'authentification, procédez comme suit:

  1. Accédez au fichier src/components/Header.jsx.
  2. Remplacez la fonction useUserSession par le code suivant:
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;
}

Ce code utilise un hook d'état React pour mettre à jour l'utilisateur lorsque la fonction onAuthStateChanged spécifie qu'il y a un changement de l'état d'authentification.

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 qu'accessoire.

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

Cela signifie que le composant <Header> affiche les données utilisateur, le cas échéant, pendant l'exécution du serveur. Si des mises à jour d'authentification sont effectuées 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 "Afficher l'état de connexion", puis transférez-le vers votre dépôt GitHub.
  2. Ouvrez la page Hébergement d'applications dans la console Firebase, puis attendez que le déploiement soit terminé.
  3. Vérifiez le nouveau comportement d'authentification:
    1. Dans votre navigateur, actualisez l'application Web. Votre nom à afficher s'affiche dans l'en-tête.
    2. Déconnectez-vous, puis reconnectez-vous. La page est mise à jour en temps réel sans actualisation. 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 la source HTML brute renvoyée 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 fictives sur des restaurants dans votre base de données Cloud Firestore locale, procédez comme suit:

  1. Dans l'application Web, sélectionnez 2cf67d488d8e6332.png > Ajouter des exemples de restaurants.
  2. Dans la console Firebase, sur la page Base de données Firestore, sélectionnez restaurants. Vous voyez les documents de premier niveau de la collection de restaurants, chacun représentant un restaurant.
  3. Cliquez sur quelques documents pour explorer les propriétés d'un document "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 de récupération 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 au moment de l'exécution du serveur. Vous allez implémenter la fonction getRestaurants en suivant les étapes ci-dessous.
  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 "Lire la liste des restaurants à partir de Firestore", puis transférez-le vers votre dépôt GitHub.
  2. Ouvrez la page Hébergement d'applications dans la console Firebase, puis attendez que le déploiement soit terminé.
  3. Dans l'application Web, actualisez la page. Les images des restaurants s'affichent sous forme de cartes sur la page.

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

Avec le framework Next.js, il peut être difficile de savoir quand les données sont chargées au moment de l'exécution du serveur ou côté client.

Pour vérifier que les fiches de restaurant se chargent au moment 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 DevTools

  1. Actualisez l'application Web. Les fiches de restaurant continuent de s'afficher. 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 des 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 et 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 et 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 par le 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(() => {
        const unsubscribe = getRestaurantsSnapshot(data => {
                setRestaurants(data);
        }, filters);

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

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

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

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

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

  1. Créez un commit avec le message "Écouter les mises à jour des restaurants en temps réel" et transférez-le vers votre dépôt GitHub.
  2. Ouvrez la page Hébergement d'applications dans la console Firebase, puis attendez que le déploiement soit terminé.
  3. Dans l'application Web, sélectionnez 27ca5d1e8ed8adfe.png > Ajouter des exemples de restaurants. Si votre fonction d'instantané est implémentée correctement, 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 des chiffres mis à jour pour le nombre de notes et 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 du formulaire, comme data.get("text") pour obtenir la valeur textuelle de la charge utile d'envoi du formulaire.

Pour utiliser une action de serveur Next.js pour 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 pour un restaurant

Vous avez implémenté la possibilité d'envoyer des 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 "Permettre aux utilisateurs d'envoyer des avis sur les restaurants", puis transférez-le vers votre dépôt GitHub.
  2. Ouvrez la page Hébergement d'applications dans la console Firebase, puis attendez que le 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.
  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 Start collection (Commencer une collection), sélectionnez ratings (Avis).
  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 représentant 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);
        setRestaurant({ ...restaurant, photo: imageURL });
}

Aucune modification n'est requise, mais vous implémentez 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 mise à jour.

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

Pour vérifier que l'image est importée comme prévu, procédez comme suit:

  1. Créez un commit avec le message "Permettre aux utilisateurs de modifier la photo de chaque restaurant", puis transférez-le vers votre dépôt GitHub.
  2. Ouvrez la page Hébergement d'applications dans la console Firebase, puis attendez que le 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, puis 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 sa mise en ligne.
  5. Accédez à Cloud Storage for Firebase.
  6. Accédez au dossier représentant le restaurant. L'image que vous avez importée existe dans le dossier.

6cf3f9e2303c931c.png

10. Résumer les avis sur un restaurant 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. Créez une clé dans Google AI Studio.
  2. 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 suivante 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é à 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 d'hébergement d'applications.

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 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. Créez un commit avec le message "Utiliser l'IA pour résumer les avis" et déployez-le dans votre dépôt GitHub.
  3. Ouvrez la page Hébergement d'applications dans la console Firebase, puis attendez que le déploiement soit terminé.
  4. Ouvrez la page d'un restaurant. En haut de la page, vous devriez voir un résumé en une phrase de tous les avis de la page.
  5. Ajoutez un avis et actualisez la page. Le résumé devrait changer.

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