Integrare Firebase con un'app Next.js

1. Prima di iniziare

In questo codelab imparerai a integrare Firebase con un'app web Next.js chiamata Friendly Eats, un sito web di recensioni di ristoranti.

App web Friendly Eats

L'app web completata offre funzionalità utili che dimostrano come Firebase può aiutarti a creare app Next.js. Queste funzionalità includono:

  • Compilazione ed esecuzione automatica: questo codelab utilizza Firebase App Hosting per compilare ed eseguire automaticamente il codice Next.js ogni volta che esegui il push in un ramo configurato.
  • Accesso e uscita: l'app web completata ti consente di accedere con Google e di uscire. L'accesso e la persistenza degli utenti sono gestiti interamente tramite Firebase Authentication.
  • Immagini:l'app web completata consente agli utenti che hanno eseguito l'accesso di caricare le immagini del ristorante. Gli asset immagine vengono archiviati in Cloud Storage for Firebase. L'SDK Firebase JavaScript fornisce un URL pubblico alle immagini caricate. Questo URL pubblico viene poi archiviato nel documento del ristorante pertinente in Cloud Firestore.
  • Recensioni: l'app web completata consente agli utenti che hanno eseguito l'accesso di pubblicare recensioni dei ristoranti costituite da una valutazione a stelle e da un messaggio di testo. Le informazioni sulle recensioni vengono archiviate in Cloud Firestore.
  • Filtri:l'app web completata consente agli utenti che hanno eseguito l'accesso di filtrare l'elenco dei ristoranti in base a categoria, località e prezzo. Puoi anche personalizzare il metodo di ordinamento utilizzato. I dati vengono acceduti da Cloud Firestore e le query di Firestore vengono applicate in base ai filtri utilizzati.

Prerequisiti

  • Un account GitHub
  • Conoscenza di Next.js e JavaScript

Obiettivi didattici

  • Come utilizzare Firebase con il router app Next.js e il rendering lato server.
  • Come mantenere le immagini in Cloud Storage for Firebase.
  • Come leggere e scrivere dati in un database Cloud Firestore.
  • Come utilizzare Accedi con Google con l'SDK Firebase JavaScript.

Che cosa ti serve

  • Git
  • Una versione stabile recente di Node.js
  • Un browser a tua scelta, ad esempio Google Chrome
  • Un ambiente di sviluppo con un editor di codice e un terminale
  • Un Account Google per la creazione e la gestione del tuo progetto Firebase
  • La possibilità di eseguire l'upgrade del progetto Firebase al piano tariffario Blaze

2. Configura l'ambiente di sviluppo e il repository GitHub

Questo codelab fornisce il codice di base dell'app e si basa sull'interfaccia a riga di comando di Firebase.

Creare un repository GitHub

Il codice sorgente del codelab è disponibile all'indirizzo https://github.com/firebase/friendlyeats-web. Il repository contiene progetti di esempio per più piattaforme. Tuttavia, questo codelab utilizza solo la directory nextjs-start. Prendi nota delle seguenti directory:

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

Copia la cartella nextjs-start nel tuo repository:

  1. Utilizzando un terminale, crea una nuova cartella sul computer e passa alla nuova directory:
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. Utilizza il pacchetto npm giget per recuperare solo la cartella nextjs-start:
    npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
    
  3. Monitora le modifiche localmente con git:
    git init
    
    git commit -a -m "codelab starting point"
    
    git branch -M main
    
  4. Crea un nuovo repository GitHub: https://github.com/new. Assegna un nome a piacere.
    1. GitHub ti fornirà un nuovo URL del repository simile a https://github.com//.git o git@github.com:/.git. Copia questo URL.
  5. Esegui il push delle modifiche locali nel nuovo repository GitHub. Esegui il comando seguente sostituendo l'URL del tuo repository al segnaposto .
    git remote add origin <your-repository-url>
    
    git push -u origin main
    
  6. Ora dovresti vedere il codice di avvio nel tuo repository GitHub.

Installa o aggiorna l'interfaccia a riga di comando di Firebase

Esegui il seguente comando per verificare di avere installato l'interfaccia a riga di comando di Firebase e che sia la versione 13.9.0 o successiva:

firebase --version

Se vedi una versione precedente o non hai installato Firebase CLI, esegui il comando di installazione:

npm install -g firebase-tools@latest

Se non riesci a installare l'interfaccia a riga di comando di Firebase a causa di errori di autorizzazione, consulta la documentazione di npm o utilizza un'altra opzione di installazione.

Accedi a Firebase

  1. Esegui il seguente comando per accedere all'interfaccia a riga di comando di Firebase:
    firebase login
    
  2. A seconda che tu voglia che Firebase raccolga i dati, inserisci Y o N.
  3. Nel browser, seleziona il tuo Account Google e fai clic su Consenti.

3. Configura il progetto Firebase

In questa sezione, configurerai un progetto Firebase e ne associerai un'app web Firebase. Configura anche i servizi Firebase utilizzati dall'app web di esempio.

Crea un progetto Firebase

  1. Nella console Firebase, fai clic su Aggiungi progetto.
  2. Nella casella di testo Inserisci il nome del progetto, inserisci FriendlyEats Codelab (o il nome del progetto che preferisci) e fai clic su Continua.
  3. Nella finestra modale Conferma piano di fatturazione Firebase, verifica che il piano sia Blaze e poi fai clic su Conferma piano.
  4. Per questo codelab non è necessario Google Analytics, quindi disattiva l'opzione Abilita Google Analytics per questo progetto.
  5. Fai clic su Crea progetto.
  6. Attendi il provisioning del progetto, poi fai clic su Continua.
  7. Nel progetto Firebase, vai a Impostazioni progetto. Prendi nota dell'ID progetto, perché ti servirà in seguito. Questo identificatore univoco serve a identificare il progetto (ad esempio nella CLI di Firebase).

Eseguire l'upgrade del piano tariffario di Firebase

Per utilizzare Firebase App Hosting e Cloud Storage for Firebase, il tuo progetto Firebase deve utilizzare il piano tariffario Blaze con pagamento a consumo, il che significa che deve essere collegato a un account di fatturazione Cloud.

  • Un account di fatturazione Cloud richiede un metodo di pagamento, ad esempio una carta di credito.
  • Se non hai mai utilizzato Firebase e Google Cloud, controlla se hai l'idoneità a ricevere un credito di 300$e un account di fatturazione Cloud per la prova senza costi.
  • Se stai svolgendo questo codelab nell'ambito di un evento, chiedi all'organizzatore se sono disponibili crediti Cloud.

Per eseguire l'upgrade del progetto al piano Blaze:

  1. Nella console Firebase, seleziona l'upgrade del piano.
  2. Seleziona il piano Blaze. Segui le istruzioni sullo schermo per collegare un account di fatturazione Cloud al tuo progetto.
    Se hai dovuto creare un account di fatturazione Cloud nell'ambito di questo upgrade, potresti dover tornare al flusso di upgrade nella console Firebase per completarlo.

Aggiungere un'app web al progetto Firebase

  1. Vai a Panoramica del progetto nel tuo progetto Firebase, poi fai clic su e41f2efdd9539c31.png Web.

    Se hai già registrato app nel tuo progetto, fai clic su Aggiungi app per visualizzare l'icona Web.
  2. Nella casella di testo Nickname app, inserisci un nickname per l'app facile da ricordare, ad esempio My Next.js app.
  3. Mantieni deselezionata la casella di controllo Configura anche Firebase Hosting per questa app.
  4. Fai clic su Registra app > Avanti > Avanti > Vai alla console.

Configurare i servizi Firebase nella console Firebase

Configurare l'autenticazione

  1. Nella Console Firebase, vai ad Autenticazione.
  2. Fai clic su Inizia.
  3. Nella colonna Fornitori aggiuntivi, fai clic su Google > Attiva.
  4. Nella casella di testo Nome visibile al pubblico del progetto, inserisci un nome facile da ricordare, ad esempio My Next.js app.
  5. Nel menu a discesa Email dell'assistenza per il progetto, seleziona il tuo indirizzo email.
  6. Fai clic su Salva.

Configura Cloud Firestore

  1. Nel riquadro a sinistra della console Firebase, espandi Build e seleziona Database Firestore.
  2. Fai clic su Crea database.
  3. Lascia l'ID database impostato su (default).
  4. Seleziona una posizione per il database, poi fai clic su Avanti.
    Per un'app reale, scegli una posizione vicina ai tuoi utenti.
  5. Fai clic su Avvia in modalità di test. Leggi il disclaimer relativo alle regole di sicurezza.
    Più avanti in questo codelab, aggiungerai le regole di sicurezza per proteggere i tuoi dati. Non distribuire o esporre pubblicamente un'app senza aggiungere regole di sicurezza per il tuo database.
  6. Fai clic su Crea.

Configurare Cloud Storage for Firebase

  1. Nel riquadro a sinistra della console Firebase, espandi Build e seleziona Archiviazione.
  2. Fai clic su Inizia.
  3. Seleziona una posizione per il bucket di archiviazione predefinito.
    I bucket in US-WEST1, US-CENTRAL1 e US-EAST1 possono usufruire del livello"Sempre senza costi" per Google Cloud Storage. I bucket in tutte le altre località rispettano i prezzi e l'utilizzo di Google Cloud Storage.
  4. Fai clic su Avvia in modalità di test. Leggi il disclaimer relativo alle regole di sicurezza.
    Più avanti in questo codelab, aggiungerai regole di sicurezza per proteggere i tuoi dati. Non distribuire o esporre un'app pubblicamente senza aggiungere regole di sicurezza per il bucket di archiviazione.
  5. Fai clic su Crea.

4. Esamina il codice di base di avvio

In questa sezione esaminerai alcune aree della base di codice di avvio dell'app a cui aggiungerai funzionalità in questo codelab.

Struttura di cartelle e file

La tabella seguente contiene una panoramica della struttura di file e cartelle dell'app:

Cartelle e file

Descrizione

src/components

Componenti React per filtri, intestazioni, dettagli del ristorante e recensioni

src/lib

Funzioni di utilità non necessariamente legate a React o Next.js

src/lib/firebase

Codice e configurazione Firebase specifici

public

Asset statici nell'app web, come le icone

src/app

Routing con il router dell'app Next.js

src/app/restaurant

Un gestore di route API

package.json e package-lock.json

Dipendenze del progetto con npm

next.config.js

Configurazione specifica di Next.js (le azioni del server sono attivate)

jsconfig.json

Configurazione del servizio linguistico JavaScript

Componenti server e client

L'app è un'app web Next.js che utilizza App Router. Il rendering lato server viene utilizzato in tutta l'app. Ad esempio, il file src/app/page.js è un componente del server responsabile della pagina principale. Il file src/components/RestaurantListings.jsx è un componente client indicato dalla direttiva "use client" all'inizio del file.

Importa estratti conto

Potresti notare dichiarazioni di importazione come la seguente:

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

L'app utilizza il simbolo @ per evitare percorsi di importazione relativi complicati ed è resa possibile dagli alias di percorso.

API specifiche di Firebase

Tutto il codice dell'API Firebase è racchiuso nella directory src/lib/firebase. I singoli componenti React importano quindi le funzioni con wrapping dalla directory src/lib/firebase, anziché importare direttamente le funzioni Firebase.

Dati simulati

I dati del ristorante e delle recensioni simulati sono contenuti nel file src/lib/randomData.js. I dati di questo file vengono assemblati nel codice del file src/lib/fakeRestaurants.js.

5. Crea un backend di hosting delle app

In questa sezione configurerai un backend App Hosting per monitorare un ramo nel tuo repository Git.

Al termine di questa sezione, avrai un backend di App Hosting collegato al tuo repository su GitHub che ricostruirà e implementerà automaticamente una nuova versione della tua app ogni volta che esegui il push di un nuovo commit nel ramo main.

Esegui il deployment delle regole di sicurezza

Il codice contiene già insiemi di regole di sicurezza per Firestore e Cloud Storage for Firebase. Dopo aver implementato le regole di sicurezza, i dati nel database e nel bucket sono protetti meglio da usi impropri.

  1. Nel terminale, configura l'interfaccia a riga di comando in modo da utilizzare il progetto Firebase creato in precedenza:
    firebase use --add
    
    Quando ti viene richiesto un alias, inserisci friendlyeats-codelab.
  2. Per eseguire il deployment di queste regole di sicurezza, esegui questo comando nel terminale:
    firebase deploy --only firestore:rules,storage
    
  3. Se ti viene chiesto: "Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?", premi Enter per selezionare .

Aggiungi la configurazione di Firebase al codice dell'app web

  1. Nella Console Firebase, vai alle Impostazioni progetto.
  2. Nel riquadro Configurazione e impostazione dell'SDK, fai clic su "Aggiungi app" e poi sull'icona delle parentesi graffe del codice per registrare una nuova app web.
  3. Al termine del flusso di creazione dell'app web, copia la variabile firebaseConfig e le relative proprietà e i relativi valori.
  4. Apri il file apphosting.yaml nell'editor di codice e compila i valori delle variabili di ambiente con i valori di configurazione della Console Firebase.
  5. Nel file, sostituisci le proprietà esistenti con quelle che hai copiato.
  6. Salva il file.

Crea un backend

  1. Vai alla pagina Hosting app nella Console Firebase:

Lo stato zero della console di hosting di app, con un pulsante &quot;Inizia&quot;

  1. Fai clic su "Inizia" per avviare il flusso di creazione del backend. Configura il backend come segue:
  2. Segui le istruzioni nel primo passaggio per collegare il repository GitHub creato in precedenza.
  3. Imposta le impostazioni di deployment:
    1. Mantieni la directory principale come /
    2. Imposta il ramo attivo su main
    3. Abilita implementazioni automatiche
  4. Assegna un nome al backend friendlyeats-codelab.
  5. In "Crea o associa un'app web Firebase", scegli l'app web che hai configurato in precedenza dal menu a discesa "Seleziona un'app web Firebase esistente".
  6. Fai clic su "Fine ed esegui il deployment". Dopo un istante, verrà visualizzata una nuova pagina in cui potrai vedere lo stato del nuovo backend di App Hosting.
  7. Al termine dell'implementazione, fai clic sul tuo dominio senza costi in "domini". L'inizio del funzionamento potrebbe richiedere alcuni minuti a causa della propagazione del DNS.

Hai eseguito il deployment dell'app web iniziale. Ogni volta che esegui il push di un nuovo commit nel ramo main del tuo repository GitHub, nella Console Firebase viene avviata una nuova build e un nuovo rollout e il tuo sito verrà aggiornato automaticamente al termine dell'implementazione.

6. Aggiungere l'autenticazione all'app web

In questa sezione aggiungi l'autenticazione all'app web per potervi accedere.

Implementa le funzioni di accesso e disconnessione

  1. Nel file src/lib/firebase/auth.js, sostituisci le funzioni onAuthStateChanged, signInWithGoogle e signOut con il seguente codice:
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);
  }
}

Questo codice utilizza le seguenti API Firebase:

API Firebase

Descrizione

GoogleAuthProvider

Crea un'istanza del provider di autenticazione Google.

signInWithPopup

Avvia un flusso di autenticazione basato su dialogo.

auth.signOut

L'utente viene disconnesso.

Nel file src/components/Header.jsx, il codice richiama già le funzioni signInWithGoogle e signOut.

  1. Crea un commit con il messaggio "Aggiunta dell'autenticazione Google" ed esegui il push nel tuo repository GitHub. 1. Apri la pagina Hosting app nella Console Firebase e attendi il completamento del nuovo implementazione.
  2. Nell'app web, aggiorna la pagina e fai clic su Accedi con Google. L'app web non si aggiorna, quindi non è chiaro se l'accesso è andato a buon fine.

Invia lo stato di autenticazione al server

Per passare lo stato di autenticazione al server, utilizzeremo un service worker. Sostituisci le funzioni fetchWithFirebaseHeaders e getAuthIdToken con il seguente codice:

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

Leggi lo stato di autenticazione sul server

Utilizzeremo FirebaseServerApp per rispecchiare lo stato di autenticazione del client sul server.

Apri src/lib/firebase/serverApp.js e sostituisci la funzione 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 };
}

Iscriversi alle modifiche all'autenticazione

Per iscriverti alle modifiche all'autenticazione:

  1. Vai al file src/components/Header.jsx.
  2. Sostituisci la funzione useUserSession con il seguente codice:
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;
}

Questo codice utilizza un hook state di React per aggiornare l'utente quando la funzione onAuthStateChanged specifica che è stata apportata una modifica allo stato di autenticazione.

Verificare le modifiche

Il layout principale nel file src/app/layout.js esegue il rendering dell'intestazione e passa l'utente, se disponibile, come prop.

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

Ciò significa che il componente <Header> esegue il rendering dei dati utente, se disponibili, durante il tempo di esecuzione del server. Se durante il ciclo di vita della pagina si verificano aggiornamenti dell'autenticazione dopo il caricamento iniziale della pagina, vengono gestiti dall'handler onAuthStateChanged.

Ora è il momento di implementare una nuova build e verificare ciò che hai creato.

  1. Crea un commit con il messaggio "Mostra stato accesso" ed esegui il push nel tuo repository GitHub.
  2. Apri la pagina Hosting app nella Console Firebase e attendi il completamento del nuovo implementazione.
  3. Verifica il nuovo comportamento di autenticazione:
    1. Nel browser, aggiorna l'app web. Il tuo nome visualizzato viene visualizzato nell'intestazione.
    2. Esci e accedi di nuovo. La pagina viene aggiornata in tempo reale senza un aggiornamento della pagina. Puoi ripetere questo passaggio con utenti diversi.
    3. (Facoltativo) Fai clic con il tasto destro del mouse sull'app web, seleziona Visualizza il codice sorgente della pagina e cerca il nome visualizzato. Viene visualizzato nel codice sorgente HTML non elaborato restituito dal server.

7. Visualizzare le informazioni del ristorante

L'app web include dati simulati per ristoranti e recensioni.

Aggiungi uno o più ristoranti

Per inserire dati simulati dei ristoranti nel tuo database Cloud Firestore locale:

  1. Nell'app web, seleziona 2cf67d488d8e6332.png > Aggiungi ristoranti di esempio.
  2. Nella console Firebase, nella pagina Database Firestore, seleziona ristoranti. Nella raccolta dei ristoranti vengono visualizzati i documenti di primo livello, ciascuno dei quali rappresenta un ristorante.
  3. Fai clic su alcuni documenti per esplorare le proprietà di un documento del ristorante.

Visualizza l'elenco dei ristoranti

Il tuo database Cloud Firestore ora contiene i ristoranti che l'app web Next.js può mostrare.

Per definire il codice di recupero dei dati:

  1. Nel file src/app/page.js, individua il componente del server <Home /> e controlla la chiamata alla funzione getRestaurants, che recupera un elenco di ristoranti in fase di esecuzione del server. Implementa la funzione getRestaurants nei passaggi che seguono.
  2. Nel file src/lib/firebase/firestore.js, sostituisci le funzioni applyQueryFilters e getRestaurants con il seguente codice:
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. Crea un commit con il messaggio "Leggi l'elenco dei ristoranti da Firestore" ed esegui il push nel tuo repository GitHub.
  2. Apri la pagina Hosting app nella Console Firebase e attendi il completamento del nuovo implementazione.
  3. Nell'app web, aggiorna la pagina. Le immagini dei ristoranti vengono visualizzate sotto forma di riquadri nella pagina.

Verificare che le schede dei ristoranti vengano caricate in fase di esecuzione del server

Quando utilizzi il framework Next.js, potrebbe non essere chiaro quando i dati vengono caricati in fase di esecuzione del server o lato client.

Per verificare che le schede dei ristoranti vengano caricate in fase di esecuzione del server:

  1. Nell'app web, apri DevTools e disattiva JavaScript.

Disattivare JavaScript in DevTools

  1. Aggiorna l'app web. Le schede dei ristoranti vengono comunque caricate. Le informazioni sul ristorante vengono restituite nella risposta del server. Quando JavaScript è attivato, le informazioni sul ristorante vengono rigenerate tramite il codice JavaScript lato client.
  2. In DevTools, riattiva JavaScript.

Monitora gli aggiornamenti dei ristoranti con i listener di snapshot di Cloud Firestore

Nella sezione precedente, hai visto come viene caricato l'insieme iniziale di ristoranti dal file src/app/page.js. Il file src/app/page.js è un componente del server e viene visualizzato sul server, incluso il codice di recupero dei dati di Firebase.

Il file src/components/RestaurantListings.jsx è un componente client e può essere configurato per eseguire l'idratazione del markup visualizzato sul server.

Per configurare il file src/components/RestaurantListings.jsx in modo da eseguire l'idratazione del markup visualizzato dal server:

  1. Nel file src/components/RestaurantListings.jsx, osserva il seguente codice, che è già stato scritto per te:
useEffect(() => {
        const unsubscribe = getRestaurantsSnapshot(data => {
                setRestaurants(data);
        }, filters);

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

Questo codice richiama la funzione getRestaurantsSnapshot(), simile alla funzione getRestaurants() implementata in un passaggio precedente. Tuttavia, questa funzione di snapshot fornisce un meccanismo di callback in modo che venga richiamato ogni volta che viene apportata una modifica alla raccolta del ristorante.

  1. Nel file src/lib/firebase/firestore.js, sostituisci la funzione getRestaurantsSnapshot() con il seguente codice:
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;
}

Le modifiche apportate tramite la pagina Database Firestore ora vengono applicate all'app web in tempo reale.

  1. Crea un commit con il messaggio "Ascolta gli aggiornamenti in tempo reale dei ristoranti" ed esegui il push nel tuo repository GitHub.
  2. Apri la pagina Hosting app nella Console Firebase e attendi il completamento del nuovo implementazione.
  3. Nell'app web, seleziona 27ca5d1e8ed8adfe.png > Aggiungi ristoranti di esempio. Se la funzione di istantanea è implementata correttamente, i ristoranti vengono visualizzati in tempo reale senza aggiornare la pagina.

8. Salvare le recensioni inviate dagli utenti dall'app web

  1. Nel file src/lib/firebase/firestore.js, sostituisci la funzione updateWithRating() con il seguente codice:
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()),
	});
};

Questo codice inserisce un nuovo documento Firestore che rappresenta la nuova recensione. Il codice aggiorna anche il documento Firestore esistente che rappresenta il ristorante con dati aggiornati sul numero di valutazioni e sulla valutazione media calcolata.

  1. Sostituisci la funzione addReviewToRestaurant() con il seguente codice:
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;
	}
}

Implementare un'azione del server Next.js

Un'azione del server Next.js fornisce un'API pratica per accedere ai dati del modulo, ad esempio data.get("text") per ottenere il valore del testo dal payload di invio del modulo.

Per utilizzare un'azione server Next.js per elaborare l'invio del modulo di revisione:

  1. Nel file src/components/ReviewDialog.jsx, individua l'attributo action nell'elemento <form>.
<form action={handleReviewFormSubmission}>

Il valore dell'attributo action si riferisce a una funzione che implementi nel passaggio successivo.

  1. Nel file src/app/actions.js, sostituisci la funzione handleReviewFormSubmission() con il seguente codice:
// 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"),
        });
}

Aggiungere recensioni per un ristorante

Hai implementato il supporto per l'invio delle recensioni, quindi ora puoi verificare che le recensioni vengano inserite correttamente in Cloud Firestore.

Per aggiungere una recensione e verificare che sia stata inserita in Cloud Firestore:

  1. Crea un commit con il messaggio "Consenti agli utenti di inviare recensioni dei ristoranti" ed esegui il push nel tuo repository GitHub.
  2. Apri la pagina Hosting app nella Console Firebase e attendi il completamento del nuovo implementazione.
  3. Aggiorna l'app web e seleziona un ristorante dalla home page.
  4. Nella pagina del ristorante, fai clic su 3e19beef78bb0d0e.png.
  5. Seleziona una valutazione a stelle.
  6. Scrivi una recensione.
  7. Fai clic su Invia. La tua recensione viene visualizzata nella parte superiore dell'elenco delle recensioni.
  8. In Cloud Firestore, cerca il documento del ristorante che hai recensito nel riquadro Aggiungi documento e selezionalo.
  9. Nel riquadro Avvia raccolta, seleziona valutazioni.
  10. Nel riquadro Aggiungi documento, individua il documento da esaminare per verificare che sia stato inserito come previsto.

Documenti nell&#39;emulatore Firestore

9. Salvare i file caricati dagli utenti dall'app web

In questa sezione aggiungi la funzionalità che ti consente di sostituire l'immagine associata a un ristorante dopo aver eseguito l'accesso. Carichi l'immagine su Firebase Storage e aggiorni l'URL dell'immagine nel documento Cloud Firestore che rappresenta il ristorante.

Per salvare i file caricati dagli utenti dall'app web:

  1. Nel file src/components/Restaurant.jsx, osserva il codice che viene eseguito quando l'utente carica un file:
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 });
}

Non sono necessarie modifiche, ma devi implementare il comportamento della funzione updateRestaurantImage() nei passaggi che seguono.

  1. Nel file src/lib/firebase/storage.js, sostituisci le funzioni updateRestaurantImage() e uploadImage() con il seguente codice:
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 funzione updateRestaurantImageReference() è già stata implementata per te. Questa funzione aggiorna un documento del ristorante esistente in Cloud Firestore con un URL immagine aggiornato.

Verifica la funzionalità di caricamento delle immagini

Per verificare che l'immagine venga caricata come previsto:

  1. Crea un commit con il messaggio "Consenti agli utenti di modificare la foto di ogni ristorante" ed esegui il push nel tuo repository GitHub.
  2. Apri la pagina Hosting app nella Console Firebase e attendi il completamento del nuovo implementazione.
  3. Nell'app web, verifica di aver eseguito l'accesso e seleziona un ristorante.
  4. Fai clic su 7067eb41fea41ff0.png e carica un'immagine dal tuo file system. L'immagine esce dall'ambiente locale e viene caricata su Cloud Storage. L'immagine viene visualizzata immediatamente dopo il caricamento.
  5. Vai a Cloud Storage per Firebase.
  6. Vai alla cartella che rappresenta il ristorante. L'immagine che hai caricato esiste nella cartella.

6cf3f9e2303c931c.png

10. Riepilogare le recensioni dei ristoranti con l'AI generativa

In questa sezione aggiungerai una funzionalità di riepilogo delle recensioni in modo che un utente possa capire rapidamente cosa pensano tutti di un ristorante senza dover leggere ogni recensione.

Memorizzare una chiave API Gemini in Cloud Secret Manager

  1. Per utilizzare l'API Gemini, devi disporre di una chiave API. Crea una chiave in Google AI Studio.
  2. Hosting delle app si integra con Cloud Secret Manager per consentirti di archiviare in modo sicuro valori sensibili come le chiavi API:
    1. In un terminale, esegui il comando per creare un nuovo secret:
    firebase apphosting:secrets:set gemini-api-key
    
    1. Quando ti viene richiesto il valore del secret, copia e incolla la chiave API Gemini da Google AI Studio.
    2. Quando ti viene chiesto se il nuovo segreto deve essere aggiunto a apphosting.yaml, inserisci Y per accettare.

La chiave API Gemini è ora archiviata in modo sicuro in Cloud Secret Manager ed è accessibile al backend di App Hosting.

Implementare il componente di riepilogo delle recensioni

  1. In src/components/Reviews/ReviewSummary.jsx, sostituisci la funzione GeminiSummary con il seguente codice:
    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. Crea un commit con il messaggio "Utilizza l'IA per riassumere le recensioni" ed esegui il push nel tuo repository GitHub.
  3. Apri la pagina Hosting app nella Console Firebase e attendi il completamento del nuovo implementazione.
  4. Apri una pagina di un ristorante. In alto, dovresti vedere un riepilogo di una frase di tutte le recensioni nella pagina.
  5. Aggiungi una nuova recensione e aggiorna la pagina. Dovresti vedere la modifica del riepilogo.

11. Conclusione

Complimenti! Hai imparato a utilizzare Firebase per aggiungere funzionalità a un'app Next.js. Nello specifico, hai utilizzato quanto segue:

Scopri di più