Integra Firebase a una app de Next.js

1. Antes de comenzar

En este codelab, aprenderás a integrar Firebase a una app web de Next.js llamada Friendly Eats, que es un sitio web para opiniones sobre restaurantes.

App web de Friendly Eats

La app web completa ofrece funciones útiles que demuestran cómo Firebase puede ayudarte a compilar apps de Next.js. Entre estas características, se incluyen las siguientes:

  • Función Acceder con Google y salir: La aplicación web completa te permite acceder con Google y salir. El acceso y la persistencia de los usuarios se administran completamente a través de Firebase Authentication.
  • Imágenes: La aplicación web completa permite que los usuarios que hayan accedido a su cuenta suban imágenes de restaurantes. Los recursos de imagen se almacenan en Cloud Storage para Firebase. El SDK de Firebase JavaScript proporciona una URL pública a las imágenes subidas. Luego, esta URL pública se almacena en el documento relevante del restaurante en Cloud Firestore.
  • Opiniones: La aplicación web completa permite que los usuarios que accedieron a su cuenta publiquen opiniones de restaurantes que constan de una calificación por estrellas y un mensaje de texto. La información de las opiniones se almacena en Cloud Firestore.
  • Filtros: La app web completa permite que los usuarios que hayan accedido a su cuenta filtren la lista de restaurantes según su categoría, ubicación y precio. También puedes personalizar el método de ordenamiento utilizado. Se accede a los datos desde Cloud Firestore, y las consultas de Firestore se aplican según los filtros que se usen.

Requisitos previos

  • Conocimientos de Next.js y JavaScript

Qué aprenderás

  • Cómo usar Firebase con el router de app Next.js y la renderización del servidor
  • Cómo conservar imágenes en Cloud Storage para Firebase.
  • Cómo leer y escribir datos en una base de datos de Cloud Firestore
  • Cómo usar el acceso con Google mediante el SDK de Firebase JavaScript

Requisitos

  • Git
  • Java Development Kit
  • Una versión estable reciente de Node.js
  • Un navegador de tu elección, como Google Chrome
  • Un entorno de desarrollo con un editor de código y una terminal
  • Una Cuenta de Google para la creación y administración de tu proyecto de Firebase
  • La capacidad de actualizar tu proyecto de Firebase al plan de precios Blaze

2. Configura tu entorno de desarrollo

En este codelab, se proporciona la base de código de partida de la app y se basa en Firebase CLI.

Descarga el repositorio

  1. En tu terminal, clona el repositorio de GitHub del codelab:
    git clone https://github.com/firebase/friendlyeats-web.git
    
  2. El repositorio GitHub contiene proyectos de muestra para varias plataformas. Sin embargo, este codelab usa solo el directorio nextjs-start. Toma nota de los siguientes directorios:
    • nextjs-start: Contiene el código de partida en el que se compila.
    • nextjs-end: Contiene el código de la solución de la app web finalizada.
  3. En tu terminal, navega al directorio nextjs-start y, luego, instala las dependencias necesarias:
    cd friendlyeats-web/nextjs-start
    npm install
    

Instala o actualiza Firebase CLI

Ejecuta el siguiente comando para verificar que tienes Firebase CLI instalado y que es v12.5.4 o una versión posterior:

firebase --version
  • Si tienes Firebase CLI instalado, pero no es v12.5.4 o una versión posterior, actualízalo:
    npm update -g firebase-tools
    
  • Si no tienes Firebase CLI,
    npm install -g firebase-tools
    
    instálalo.

Si no puedes instalar Firebase CLI debido a errores de permisos, consulta la documentación de npm o usa otra opción de instalación.

Accede a Firebase

  1. Ejecuta el siguiente comando para acceder a Firebase CLI:
    firebase login
    
  2. Según si deseas que Firebase recopile datos, ingresa Y o N.
  3. En el navegador, selecciona tu Cuenta de Google y, luego, haz clic en Permitir.

3. Configura el proyecto de Firebase

En esta sección, configurarás un proyecto de Firebase y lo asociarás con una aplicación web de Firebase. También configurarás los servicios de Firebase que usa la aplicación web de muestra.

Crea un proyecto de Firebase

  1. En Firebase console, haz clic en Agregar proyecto.
  2. En el cuadro de texto Ingresa el nombre de proyecto, ingresa FriendlyEats Codelab (o un nombre de proyecto que prefieras) y haz clic en Continuar.
  3. En este codelab, no necesitas Google Analytics, así que desactiva la opción Habilitar Google Analytics para este proyecto.
  4. Haz clic en Crear proyecto.
  5. Espera a que se aprovisione tu proyecto y, luego, haz clic en Continuar.
  6. En el proyecto de Firebase, ve a Configuración del proyecto. Toma nota del ID del proyecto porque lo necesitarás más adelante. Este identificador único es la forma en que se identifica tu proyecto (por ejemplo, en Firebase CLI).

Agrega una aplicación web a tu proyecto de Firebase

  1. Navega a la descripción general del proyecto en tu proyecto de Firebase y, luego, haz clic en e41f2efdd9539c31.png Web.
  2. En el cuadro de texto Sobrenombre de la app, ingresa un sobrenombre fácil de recordar, como My Next.js app.
  3. Selecciona la casilla de verificación Configurar Firebase Hosting también para esta app.
  4. Haz clic en Registrar app > Siguiente > Siguiente > Ir a la consola.

Actualiza tu plan de precios de Firebase

Para usar frameworks web, tu proyecto de Firebase debe tener el plan de precios Blaze, lo que significa que está asociado a una cuenta de Facturación de Cloud.

  • Una cuenta de Facturación de Cloud requiere una forma de pago, como una tarjeta de crédito.
  • Si es la primera vez que usas Firebase y Google Cloud, verifica si cumples con los requisitos para obtener un crédito de $300 y una cuenta de Facturación de Cloud de prueba gratuita.

Sin embargo, ten en cuenta que la finalización de este codelab no debería generar cargos reales.

Para actualizar tu proyecto al plan Blaze, sigue estos pasos:

  1. En Firebase console, selecciona la opción para actualizar tu plan.
  2. En el diálogo, selecciona el plan Blaze y, luego, sigue las instrucciones en pantalla para asociar tu proyecto con una cuenta de Facturación de Cloud.
    Si necesitas crear una cuenta de Facturación de Cloud, es posible que debas volver a la actualización. en Firebase console para completar la actualización.

Configura los servicios de Firebase en Firebase console

Configura la autenticación

  1. En Firebase console, navega a Autenticación.
  2. Haz clic en Comenzar.
  3. En la columna Proveedores adicionales, haz clic en Google > Habilitar.
  4. En el cuadro de texto Nombre público del proyecto, ingresa un nombre fácil de recordar, como My Next.js app.
  5. En el menú desplegable Correo electrónico de asistencia para el proyecto, selecciona tu dirección de correo electrónico.
  6. Haz clic en Guardar.

Configura Cloud Firestore

  1. En Firebase console, navega a Firestore.
  2. Haz clic en Crear base de datos > Comenzar en modo de prueba > Siguiente.
    Más adelante en este codelab, agregarás reglas de seguridad para proteger tus datos. No distribuyas ni expongas una app de forma pública sin agregar reglas de seguridad para tu base de datos.
  3. Usa la ubicación predeterminada o selecciona una ubicación de tu preferencia.
    En el caso de una app real, debes elegir una ubicación que esté cerca de tus usuarios. Ten en cuenta que esta ubicación no se puede cambiar más adelante y que también será automáticamente la ubicación de tu bucket predeterminado de Cloud Storage (paso siguiente).
  4. Haz clic en Listo.

Configura Cloud Storage para Firebase

  1. En Firebase console, navega a Storage.
  2. Haz clic en Comenzar > Comenzar en modo de prueba > Siguiente.
    Más adelante en este codelab, agregarás reglas de seguridad para proteger tus datos. No distribuyas ni expongas una app de forma pública sin agregar reglas de seguridad para el bucket de Storage.
  3. La ubicación del bucket ya debería estar seleccionada (debido a la configuración de Firestore en el paso anterior).
  4. Haz clic en Listo.

4. Revisa la base de código de partida

En esta sección, revisarás algunas áreas de la base de código de partida de la app a las que agregarás funcionalidad en este codelab.

Estructura de carpetas y archivos

En la siguiente tabla, se incluye una descripción general de la estructura de carpetas y archivos de la app:

Carpetas y archivos

Descripción

src/components

Componentes de React para filtros, encabezados, detalles de restaurantes y opiniones

src/lib

Funciones de utilidad que no están necesariamente vinculadas a React o Next.js

src/lib/firebase

Código específico de Firebase y configuración de Firebase

public

Recursos estáticos en la app web, como íconos

src/app

Enrutamiento con el router de app Next.js

src/app/restaurant

Un controlador de rutas de API

package.json y package-lock.json

Dependencias del proyecto con npm

next.config.js

Configuración específica de Next.js (las acciones del servidor están habilitadas)

jsconfig.json

Configuración del servicio de lenguaje JavaScript

Componentes del servidor y el cliente

La app es una app web de Next.js que usa el router de app. La renderización de servidores se usa en toda la app. Por ejemplo, el archivo src/app/page.js es un componente del servidor responsable de la página principal. El archivo src/components/RestaurantListings.jsx es un componente de cliente denotado por la directiva "use client" al comienzo del archivo.

Declaraciones de importación

Es posible que observes sentencias de importación como las siguientes:

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

La app usa el símbolo @ para evitar rutas de importación relativas toscas y es posible gracias a los alias de ruta.

APIs específicas de Firebase

Todo el código de la API de Firebase se incluye en el directorio src/lib/firebase. Luego, los componentes individuales de React importan las funciones unidas desde el directorio src/lib/firebase en lugar de importar las funciones de Firebase directamente.

Simulación de datos

Los datos ficticios de restaurantes y opiniones se encuentran en el archivo src/lib/randomData.js. Los datos de ese archivo se ensamblan en el código del archivo src/lib/fakeRestaurants.js.

5. Configura el hosting local con el emulador de Firebase Hosting

En esta sección, usarás el emulador de Firebase Hosting para ejecutar la app web Next.js de forma local.

Al final de esta sección, el emulador de Firebase Hosting ejecutará la app de Next.js por ti, por lo que no necesitarás ejecutar Next.js en un proceso independiente para los emuladores.

Descarga y usa una cuenta de servicio de Firebase

La app web que compilarás en este codelab usa la renderización del servidor con Next.js.

El SDK de Firebase Admin para Node.js se usa con el fin de garantizar que las reglas de seguridad funcionen desde el código del servidor. Para usar las APIs en Firebase Admin, debes descargar y usar una cuenta de servicio de Firebase desde Firebase console.

  1. En Firebase console, navega a la página Cuentas de servicio en la Configuración del proyecto.
  2. Haz clic en Generar nueva clave privada > Generar clave.
  3. Después de que el archivo se descargue en tu sistema de archivos, obtén la ruta completa a ese archivo.
    Por ejemplo, si descargaste el archivo en tu directorio Descargas, la ruta completa podría verse de la siguiente manera: /Users/me/Downloads/my-project-id-firebase-adminsdk-123.json
  4. En tu terminal, configura la variable de entorno GOOGLE_APPLICATION_CREDENTIALS como la ruta de acceso de la clave privada que descargaste. En un entorno Unix, el comando podría verse de la siguiente manera:
    export GOOGLE_APPLICATION_CREDENTIALS="/Users/me/Downloads/my-project-id-firebase-adminsdk-123.json"
    
  5. Mantén esta terminal abierta y úsala durante el resto de este codelab, ya que tu variable de entorno podría perderse si inicias una nueva sesión de terminal.
    Si abres una nueva sesión de la terminal, debes volver a ejecutar el comando anterior.

Agrega la configuración de Firebase al código de tu aplicación web

  1. En Firebase console, navega a Configuración del proyecto.
  2. En el panel Configuración del SDK, busca la variable firebaseConfig y copia sus propiedades y sus valores.
  3. Abre el archivo .env en el editor de código y completa los valores de la variable de entorno con los valores de configuración de Firebase console.
  4. En el archivo, reemplaza las propiedades existentes por las que copiaste.
  5. Guarda el archivo.

Inicializa la app web con tu proyecto de Firebase

Para conectar la aplicación web a tu proyecto de Firebase, sigue estos pasos:

  1. En la terminal, asegúrate de que los frameworks web estén habilitados en Firebase:
    firebase experiments:enable webframeworks
    
  2. Inicializa Firebase:
    firebase init
    
  3. Selecciona estas opciones:
    • Firestore: Configura archivos de índices y reglas de seguridad para Firestore
    • Hosting: Configura los archivos para Firebase Hosting y, de manera opcional, configura las implementaciones de GitHub Action
    • Storage: Configura un archivo de reglas de seguridad para Cloud Storage
    • Emuladores: Configura emuladores locales para productos de Firebase
  4. Selecciona Usar un proyecto existente y, luego, ingresa el ID del proyecto que anotaste antes.
  5. Selecciona los valores predeterminados para todas las preguntas posteriores hasta llegar a la pregunta ¿En qué región quisieras alojar contenido del servidor, si corresponde?. La terminal muestra un mensaje en el que se indica que detecta una base de código Next.js existente en el directorio actual.
  6. Para la pregunta ¿En qué región te gustaría alojar contenido del servidor, si corresponde?, selecciona la ubicación que seleccionaste anteriormente para Firestore y Cloud Storage.
  7. Selecciona los valores predeterminados para todas las preguntas posteriores hasta llegar a la pregunta ¿Qué emuladores de Firebase quieres configurar?. Para esta pregunta, selecciona Emulador de funciones y Emulador de Hosting.
  8. Selecciona los valores predeterminados para todas las demás preguntas.

Implementa las reglas de seguridad

El código ya tiene conjuntos de reglas de seguridad para Firestore y Cloud Storage para Firebase. Después de que implementes las reglas de seguridad, los datos en tu base de datos y tu bucket estarán más protegidos contra el uso inadecuado.

  1. Para implementar estas reglas de seguridad, ejecuta el siguiente comando en tu terminal:
    firebase deploy --only firestore:rules,storage
    
  2. Si se te pregunta lo siguiente: "Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?", selecciona .

Inicia el emulador de Hosting

  1. En la terminal, inicia el emulador de Hosting:
    firebase emulators:start --only hosting
    
    La terminal responde con el puerto en el que puedes encontrar el emulador de Hosting, por ejemplo, http://localhost:5000/.

Terminal que muestra que el emulador de Hosting está listo

  1. En tu navegador, ve a la URL con el emulador de Firebase Hosting.
  2. Si ves el error en la página web que comienza así: "Error: Firebase session cookie has incorrect...", debes borrar todas las cookies de tu entorno de localhost. Para ello, sigue las instrucciones que se indican en cómo borrar cookies | Documentación de Herramientas para desarrolladores.

Error de sesión de cookies

Cómo borrar cookies en Herramientas para desarrolladores

Ahora puedes ver la app web inicial. Aunque estés viendo la app web en una URL de localhost, esta usa los servicios reales de Firebase que configuraste en la consola.

6. Agrega autenticación a la app web

En esta sección, agregarás autenticación a la app web para que puedas acceder a ella.

Implementa las funciones de acceso y cierre de sesión

  1. En el archivo src/lib/firebase/auth.js, reemplaza las funciones onAuthStateChanged, signInWithGoogle y signOut por el siguiente código:
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);
        }
}

Este código usa las siguientes APIs de Firebase:

API de Firebase

Descripción

GoogleAuthProvider

Crea una instancia de proveedor de autenticación de Google.

signInWithPopup

Inicia un flujo de autenticación basado en diálogos.

auth.signOut

Cierra la sesión del usuario.

En el archivo src/components/Header.jsx, el código ya invoca las funciones signInWithGoogle y signOut.

  1. En la app web, actualiza la página y haz clic en Acceder con Google. La app web no se actualiza, por lo que no queda claro si el acceso se realizó correctamente.

Suscríbete a los cambios de autenticación

Para suscribirte a los cambios de autenticación, sigue estos pasos:

  1. Navega al archivo src/components/Header.jsx.
  2. Reemplaza la función useUserSession por el siguiente código:
function useUserSession(initialUser) {
        // The initialUser comes from the server through a server component
        const [user, setUser] = useState(initialUser);
        const router = useRouter();

        useEffect(() => {
                const unsubscribe = onAuthStateChanged(authUser => {
                        setUser(authUser);
                });
                return () => {
                        unsubscribe();
                };
        }, []);

        useEffect(() => {
                onAuthStateChanged(authUser => {
                        if (user === undefined) return;
                        if (user?.email !== authUser?.email) {
                                router.refresh();
                        }
                });
        }, [user]);

        return user;
}

Este código usa un hook state de React para actualizar al usuario cuando la función onAuthStateChanged especifica que se produce un cambio en el estado de autenticación.

Verifica los cambios

El diseño raíz del archivo src/app/layout.js renderiza el encabezado y pasa el usuario, si está disponible, como prop.

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

Esto significa que el componente <Header> procesa datos del usuario, si están disponibles, durante el tiempo de ejecución del servidor. Si hay actualizaciones de autenticación durante el ciclo de vida de la página después de la carga inicial de la página, el controlador onAuthStateChanged las controla.

Ahora es momento de probar la aplicación web y verificar lo que compilaste.

Para verificar el nuevo comportamiento de autenticación, sigue estos pasos:

  1. En el navegador, actualiza la app web. Tu nombre visible aparece en el encabezado.
  2. Sal y vuelve a acceder. La página se actualiza en tiempo real sin necesidad de actualizar la página. Puedes repetir este paso con diferentes usuarios.
  3. Opcional: Haz clic con el botón derecho en la aplicación web, selecciona Ver código fuente de página y busca el nombre visible. Aparecerá en la fuente HTML sin procesar que muestra el servidor.

7. Visualiza información del restaurante

La app web incluye datos de prueba de restaurantes y opiniones.

Agrega uno o más restaurantes

Para insertar datos de restaurantes ficticios en tu base de datos local de Cloud Firestore, sigue estos pasos:

  1. En la aplicación web, selecciona 2cf67d488d8e6332.png > Agregar restaurantes de muestra.
  2. En la página Base de datos de Firestore de Firebase console, selecciona restaurantes. Puedes ver los documentos de nivel superior en la colección de restaurantes, cada uno de los cuales representa un restaurante.
  3. Haz clic en algunos documentos para explorar las propiedades de un documento de restaurante.

Muestra la lista de restaurantes

Tu base de datos de Cloud Firestore ahora tiene restaurantes que la app web Next.js puede mostrar.

Para definir el código de recuperación de datos, sigue estos pasos:

  1. En el archivo src/app/page.js, busca el componente del servidor <Home /> y revisa la llamada a la función getRestaurants, que recupera una lista de restaurantes en el tiempo de ejecución del servidor. Implementarás la función getRestaurants en los siguientes pasos.
  2. En el archivo src/lib/firebase/firestore.js, reemplaza las funciones applyQueryFilters y getRestaurants por el siguiente código:
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(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. Actualiza la app web. Las imágenes de restaurantes aparecen como mosaicos en la página.

Verifica que las fichas de restaurantes se carguen en el tiempo de ejecución del servidor.

Si usas el framework Next.js, es posible que no sea evidente cuándo los datos se cargan en el entorno de ejecución del servidor o del cliente.

Para verificar que las fichas de restaurantes se carguen durante el tiempo de ejecución del servidor, sigue estos pasos:

  1. En la aplicación web, abre Herramientas para desarrolladores y, luego, inhabilita JavaScript.

Inhabilita JavaScipt en Herramientas para desarrolladores

  1. Actualiza la app web. Las fichas de restaurantes aún se cargan. La información del restaurante se muestra en la respuesta del servidor. Cuando se habilita JavaScript, la información del restaurante se hidrata a través del código JavaScript del cliente.
  2. En Herramientas para desarrolladores, vuelve a habilitar JavaScript.

Detecta actualizaciones de restaurantes con los objetos de escucha de instantáneas de Cloud Firestore

En la sección anterior, viste cómo el conjunto inicial de restaurantes se cargó desde el archivo src/app/page.js. El archivo src/app/page.js es un componente de servidor y se renderiza en el servidor, incluido el código de recuperación de datos de Firebase.

El archivo src/components/RestaurantListings.jsx es un componente del cliente y se puede configurar para hidratar el lenguaje de marcado renderizado por el servidor.

Si deseas configurar el archivo src/components/RestaurantListings.jsx para hidratar el lenguaje de marcado renderizado por el servidor, sigue estos pasos:

  1. En el archivo src/components/RestaurantListings.jsx, observa el siguiente código, que ya está escrito:
useEffect(() => {
        const unsubscribe = getRestaurantsSnapshot(data => {
                setRestaurants(data);
        }, filters);

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

Este código invoca la función getRestaurantsSnapshot(), que es similar a la función getRestaurants() que implementaste en un paso anterior. Sin embargo, esta función de resumen proporciona un mecanismo de devolución de llamada para que se invoque la devolución de llamada cada vez que se realice un cambio en la colección del restaurante.

  1. En el archivo src/lib/firebase/firestore.js, reemplaza la función getRestaurantsSnapshot() por el siguiente código:
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;
}

Los cambios realizados a través de la página base de datos de Firestore ahora se reflejan en la app web en tiempo real.

  1. En la aplicación web, selecciona 27ca5d1e8ed8adfe.png > Agregar restaurantes de muestra. Si tu función de instantánea se implementa correctamente, los restaurantes aparecen en tiempo real sin actualizar la página.

8. Guarda datos del usuario desde la app web

  1. En el archivo src/lib/firebase/firestore.js, reemplaza la función updateWithRating() por el siguiente código:
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()),
        });
};

Con este código, se inserta un documento de Firestore nuevo que representa la revisión nueva. El código también actualiza el documento existente de Firestore que representa el restaurante con cifras actualizadas para la cantidad de calificaciones y la calificación promedio calculada.

  1. Reemplaza la función addReviewToRestaurant() por el siguiente código:
export async function addReviewToRestaurant(db, restaurantId, review) {
        if (!restaurantId) {
                throw new Error("No restaurant ID was 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`)
                );

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

Implementa una acción del servidor de Next.js

Una acción del servidor de Next.js proporciona una API conveniente para acceder a datos de formularios, como data.get("text"), para obtener el valor de texto de la carga útil del envío de formulario.

Si deseas usar una acción del servidor de Next.js para procesar el envío del formulario de revisión, sigue estos pasos:

  1. En el archivo src/components/ReviewDialog.jsx, busca el atributo action en el elemento <form>.
<form action={handleReviewFormSubmission}>

El valor del atributo action hace referencia a una función que implementarás en el siguiente paso.

  1. En el archivo src/app/actions.js, reemplaza la función handleReviewFormSubmission() por el siguiente código:
// 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"),
        });
}

Agrega opiniones sobre un restaurante

Implementaste la compatibilidad con los envíos de opiniones, por lo que ahora puedes verificar que tus opiniones se inserten correctamente en Cloud Firestore.

Para agregar una opinión y verificar que se inserte en Cloud Firestore, sigue estos pasos:

  1. En la app web, selecciona un restaurante de la página principal.
  2. En la página del restaurante, haz clic en 3e19beef78bb0d0e.png.
  3. Selecciona una calificación por estrellas.
  4. Escribe una opinión
  5. Haz clic en Enviar. Tu opinión aparece en la parte superior de la lista de opiniones.
  6. En Cloud Firestore, busca en el panel Agregar documento el documento del restaurante que revisaste y selecciónalo.
  7. En el panel Iniciar colección, selecciona calificaciones.
  8. En el panel Agregar documento, busca el documento que deseas revisar para verificar que se insertó como se esperaba.

Documentos en el emulador de Firestore

9. Guarda los archivos subidos por los usuarios desde la app web

En esta sección, agregarás funcionalidad para poder reemplazar la imagen asociada con un restaurante cuando hayas accedido. Sube la imagen a Firebase Storage y actualiza la URL de la imagen en el documento de Cloud Firestore que representa al restaurante.

Para guardar los archivos subidos por los usuarios desde la aplicación web, sigue estos pasos:

  1. En el archivo src/components/Restaurant.jsx, observa el código que se ejecuta cuando el usuario sube un archivo:
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 });
}

No es necesario realizar cambios, pero implementarás el comportamiento de la función updateRestaurantImage() en los siguientes pasos.

  1. En el archivo src/lib/firebase/storage.js, reemplaza las funciones updateRestaurantImage() y uploadImage() por el siguiente código:
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 función updateRestaurantImageReference() ya está implementada. Esta función actualiza un documento de restaurante existente en Cloud Firestore con una URL de imagen actualizada.

Verifica la función de carga de imágenes

Para verificar que la imagen se suba como se espera, sigue estos pasos:

  1. En la app web, verifica que hayas accedido y selecciona un restaurante.
  2. Haz clic en 7067eb41fea41ff0.png y sube una imagen del sistema de archivos. La imagen sale de tu entorno local y se sube a Cloud Storage. La imagen aparecerá inmediatamente después de que la subas.
  3. Navega a Cloud Storage para Firebase.
  4. Navega a la carpeta que representa el restaurante. La imagen que subiste existe en la carpeta.

6cf3f9e2303c931c.png

10. Conclusión

¡Felicitaciones! Aprendiste a usar Firebase para agregar características y funcionalidades a una app de Next.js. Específicamente, usaste lo siguiente:

Más información