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.
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:
- Compilación e implementación automáticas: En este codelab, se usa Firebase App Hosting para compilar e implementar automáticamente tu código de Next.js cada vez que envías código a una rama configurada.
- Acceso y salida: La app web completada 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 app web completada permite que los usuarios que accedieron 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 para las imágenes subidas. Luego, esta URL pública se almacena en el documento relevante del restaurante en Cloud Firestore.
- Opiniones: La app web completa permite que los usuarios que accedieron a su cuenta publiquen opiniones sobre restaurantes que consisten en una calificación por estrellas y un mensaje basado en texto. La información de las opiniones se almacena en Cloud Firestore.
- Filtros: La app web completada permite que los usuarios que accedieron a su cuenta filtren la lista de restaurantes según la categoría, la ubicación y el precio. También puedes personalizar el método de ordenamiento que se usa. Se accede a los datos desde Cloud Firestore, y las consultas de Firestore se aplican según los filtros que se usen.
Requisitos previos
- Una cuenta de GitHub
- Conocimientos de Next.js y JavaScript
Qué aprenderás
- Cómo usar Firebase con el router de app y la renderización del servidor de Next.js
- 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 con el SDK de Firebase JavaScript
Requisitos
- Git
- 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 crear y administrar tu proyecto de Firebase
- La capacidad de actualizar tu proyecto de Firebase al plan de precios Blaze
2. Configura tu entorno de desarrollo y tu repositorio de GitHub
En este codelab, se proporciona la base de código de partida de la app y se basa en Firebase CLI.
Crea un repositorio de GitHub
El código fuente del codelab se puede encontrar en https://github.com/firebase/friendlyeats-web. El repositorio contiene proyectos de muestra para varias plataformas. Sin embargo, este codelab solo usa el directorio nextjs-start
. Ten en cuenta los siguientes directorios:
* `nextjs-start`: contains the starter code upon which you build.
* `nextjs-end`: contains the solution code for the finished web app.
Copia la carpeta nextjs-start
en tu propio repositorio:
- Con una terminal, crea una carpeta nueva en tu computadora y cambia al directorio nuevo:
mkdir codelab-friendlyeats-web cd codelab-friendlyeats-web
- Usa el paquete npm giget para recuperar solo la carpeta
nextjs-start
:npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
- Haz un seguimiento de los cambios de forma local con Git:
git init git add . git commit -m "codelab starting point" git branch -M main
- Crea un repositorio de GitHub nuevo: https://github.com/new. Asigna el nombre que quieras.
- Copia la nueva URL que GitHub crea para ti. Se verá de una de las siguientes maneras:
https://github.com/<USER_NAME>/<REPOSITORY_NAME>.git
ogit@github.com:<USER_NAME>/<REPOSITORY_NAME>.git
- Ejecuta el siguiente comando para enviar los cambios locales a tu nuevo repositorio de GitHub. Sustituye la URL real de tu repositorio por el marcador de posición
<REPOSITORY_URL>
.git remote add origin <REPOSITORY_URL> git push -u origin main
- Ahora deberías ver el código de partida en tu repositorio de GitHub.
Instala o actualiza Firebase CLI
Ejecuta el siguiente comando para verificar que tienes Firebase CLI instalado y que es v14.1.0 o una versión posterior:
firebase --version
Si ves una versión anterior o no tienes instalada la CLI de Firebase, ejecuta el comando de instalación:
npm install -g firebase-tools@latest
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
- Ejecuta el siguiente comando para acceder a Firebase CLI:
firebase login
- Según si deseas que Firebase recopile datos, ingresa
Y
oN
. - En tu 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 le asociarás una app web de Firebase. También configurarás los servicios de Firebase que usa la app web de muestra.
Crea un proyecto de Firebase
- Accede a la consola de Firebase con la misma Cuenta de Google que usaste en el paso anterior.
- Haz clic en el botón para crear un proyecto nuevo y, luego, ingresa un nombre (por ejemplo,
FriendlyEats Codelab
).
- Haz clic en Continuar.
- Si se te solicita, revisa y acepta las Condiciones de Firebase y, luego, haz clic en Continuar.
- (Opcional) Habilita la asistencia de IA en Firebase console (llamada "Gemini en Firebase").
- Para este codelab, no necesitas Google Analytics, por lo que debes desactivar la opción de Google Analytics.
- Haz clic en Crear proyecto, espera a que se aprovisione y, luego, haz clic en Continuar.
Actualiza tu plan de precios de Firebase
Para usar Firebase App Hosting y Cloud Storage para Firebase, tu proyecto de Firebase debe tener el plan de precios de pago por uso (Blaze), lo que significa que está vinculado 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 USD 300 y una cuenta de Facturación de Cloud de prueba gratuita.
- Si realizas este codelab como parte de un evento, pregúntale al organizador si hay créditos de Cloud disponibles.
Para actualizar tu proyecto al plan Blaze, sigue estos pasos:
- En Firebase console, selecciona la opción para actualizar tu plan.
- Selecciona el plan Blaze. Sigue las instrucciones en pantalla para vincular una cuenta de Facturación de Cloud a tu proyecto.
Si necesitas crear una cuenta de Facturación de Cloud como parte de esta actualización, es posible que debas volver al flujo de actualización en Firebase console para completar la actualización.
Agrega una app web a tu proyecto de Firebase
- Navega a la Descripción general del proyecto en tu proyecto de Firebase y, luego, haz clic en
Web.
Si ya registraste apps en tu proyecto, haz clic en Agregar app para ver el ícono de Web. - En el cuadro de texto Sobrenombre de la app, ingresa un sobrenombre memorable, como
My Next.js app
. - Deja la casilla de verificación Configurar Firebase Hosting para esta app también sin marcar.
- Haz clic en Registrar app > Ir a la consola.
Configura los servicios de Firebase en Firebase console
Configura la autenticación
- En Firebase console, navega a Autenticación.
- Haz clic en Comenzar.
- En la columna Proveedores adicionales, haz clic en Google > Habilitar.
- En el cuadro de texto Nombre público del proyecto, ingresa un nombre memorable, como
My Next.js app
. - En el menú desplegable Correo electrónico de asistencia para el proyecto, selecciona tu dirección de correo electrónico.
- Haz clic en Guardar.
Configura Cloud Firestore
- En el panel izquierdo de Firebase console, expande Compilación y, luego, selecciona Base de datos de Firestore.
- Haz clic en Crear base de datos.
- Deja el ID de la base de datos establecido en
(default)
. - Selecciona una ubicación para tu base de datos y, luego, haz clic en Siguiente.
Para una app real, debes elegir una ubicación cercana a tus usuarios. - Haz clic en Comenzar en modo de prueba. Lee la renuncia de responsabilidad sobre las reglas de seguridad.
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. - Haz clic en Crear.
Configura Cloud Storage para Firebase
- En el panel izquierdo de Firebase console, expande Compilación y, luego, selecciona Storage.
- Haz clic en Comenzar.
- Selecciona una ubicación para tu bucket de Storage predeterminado.
Los buckets enUS-WEST1
,US-CENTRAL1
yUS-EAST1
pueden aprovechar el nivel “Siempre gratuito” para Google Cloud Storage. Los buckets de todas las demás ubicaciones siguen los precios y el uso de Google Cloud Storage. - Haz clic en Comenzar en modo de prueba. Lee la renuncia de responsabilidad sobre las reglas de seguridad.
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 bucket de Storage. - Haz clic en Crear.
Implementa reglas de seguridad
El código ya tiene conjuntos de reglas de seguridad para Firestore y Cloud Storage para Firebase. Después de implementar las reglas de seguridad, los datos de tu base de datos y tu bucket estarán mejor protegidos contra el uso inadecuado.
- En tu terminal, configura la CLI para usar el proyecto de Firebase que creaste antes:
Cuando se te solicite un alias, ingresafirebase use --add
friendlyeats-codelab
. - Para implementar estas reglas de seguridad (así como los índices que se necesitarán más adelante), ejecuta este comando en tu terminal:
firebase deploy --only firestore,storage
- Si se te pregunta:
"Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?"
, presionaEnter
para seleccionar Sí.
4. Revisa la base de código de inicio
En esta sección, revisarás algunas áreas de la base de código inicial 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 |
| Componentes de React para filtros, encabezados, detalles de restaurantes y opiniones |
| Funciones de utilidad que no están necesariamente vinculadas a React o Next.js |
| Código específico de Firebase y configuración de Firebase |
| Recursos estáticos en la app web, como íconos |
| Enrutamiento con el enrutador de la app de Next.js |
| Dependencias del proyecto con npm |
| Configuración específica de Next.js (las acciones del servidor están habilitadas) |
| Configuración del servicio de lenguaje de JavaScript |
Componentes del servidor y el cliente
La app es una app web de Next.js que usa el router de app. El procesamiento del servidor 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 del cliente que se indica con la directiva "use client"
al comienzo del archivo.
Declaraciones de importación
Es posible que veas instrucciones 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 torpes, y los alias de ruta lo hacen posible.
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 encapsuladas desde el directorio src/lib/firebase
, en lugar de importar las funciones de Firebase directamente.
Datos simulados
Los datos simulados 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. Crea un backend de App Hosting
En esta sección, configurarás un backend de App Hosting para supervisar una rama en tu repositorio de Git.
Al final de esta sección, tendrás un backend de App Hosting conectado a tu repositorio en GitHub que volverá a compilar y lanzará automáticamente una nueva versión de tu app cada vez que envíes una confirmación nueva a tu rama main
.
Crear un backend
- Ve a la página App Hosting en Firebase console:
- Haz clic en "Comenzar" para iniciar el flujo de creación del backend. Configura tu backend de la siguiente manera:
- Elige una región. En una app real, elegirías la región más cercana a tus usuarios.
- Sigue las indicaciones del paso "Import a GitHub repository" para conectar el repositorio de GitHub que creaste antes.
- Establece la configuración de implementación:
- Conserva el directorio raíz como
/
. - Establece la rama activa en
main
- Habilitar los lanzamientos automáticos
- Conserva el directorio raíz como
- Asigna el nombre
friendlyeats-codelab
a tu backend. - En "Asocia una app web de Firebase", haz clic en "Crea una app web nueva de Firebase".
- Haz clic en “Finalizar e implementar”. Después de un momento, se te dirigirá a una nueva página en la que podrás ver el estado de tu nuevo backend de App Hosting.
- Una vez que se complete el lanzamiento, haz clic en tu dominio gratuito en "Dominios". Es posible que tarde unos minutos en comenzar a funcionar debido a la propagación del DNS.
- ¡Uy! Cuando cargues la página, verás un mensaje de error que dice "Error de aplicación: Se produjo una excepción del servidor (consulta los registros del servidor para obtener más información)".
- En Firebase console, revisa la pestaña "Registros" del backend de App Hosting. Verás un registro que indica "Error: not implemented". Corregiremos eso en el siguiente paso cuando agreguemos la autenticación.
Implementaste la app web inicial. Cada vez que envíes una confirmación nueva a la rama main
de tu repositorio de GitHub, verás que comienza una nueva compilación y un nuevo lanzamiento en Firebase console, y tu sitio se actualizará automáticamente cuando se complete el lanzamiento.
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.
Agregar un dominio autorizado
Firebase Authentication solo aceptará solicitudes de acceso de los dominios que permitas. Aquí, agregaremos el dominio del backend de App Hosting a la lista de dominios aprobados en tu proyecto.
- Copia el dominio del backend de App Hosting desde la página "Descripción general" de App Hosting.
- Ve a la pestaña Configuración de autenticación y elige Dominios autorizados.
- Haz clic en el botón Agregar un dominio.
- Ingresa el dominio de tu backend de App Hosting.
- Haz clic en Agregar.
Implementa las funciones de acceso y cierre de sesión
- En el archivo
src/lib/firebase/auth.js
, reemplaza las funcionesonAuthStateChanged
,onIdTokenChanged
,signInWithGoogle
ysignOut
por el siguiente código:
export function onAuthStateChanged(cb) {
return _onAuthStateChanged(auth, cb);
}
export function onIdTokenChanged(cb) {
return _onIdTokenChanged(auth, cb);
}
export async function signInWithGoogle() {
const provider = new GoogleAuthProvider();
try {
await signInWithPopup(auth, provider);
} catch (error) {
console.error("Error signing in with Google", error);
}
}
export async function signOut() {
try {
return auth.signOut();
} catch (error) {
console.error("Error signing out with Google", error);
}
}
Este código usa las siguientes APIs de Firebase:
API de Firebase | Descripción |
Agrega un observador para los cambios en el estado de acceso del usuario. | |
Agrega un observador para los cambios en el token de ID del usuario. | |
Crea una instancia del proveedor de autenticación de Google. | |
Inicia un flujo de autenticación basado en diálogos. | |
Cierra la sesión del usuario. |
En el archivo src/components/Header.jsx
, el código ya invoca las funciones signInWithGoogle
y signOut
.
Envía el estado de autenticación al servidor
Para pasar el estado de autenticación al servidor, usaremos cookies. Cada vez que cambie el estado de autenticación en el cliente, actualizaremos la cookie __session
.
En src/components/Header.jsx
, reemplaza la función useUserSession
por el siguiente código:
function useUserSession(initialUser) {
useEffect(() => {
return onIdTokenChanged(async (user) => {
if (user) {
const idToken = await user.getIdToken();
await setCookie("__session", idToken);
} else {
await deleteCookie("__session");
}
if (initialUser?.uid === user?.uid) {
return;
}
window.location.reload();
});
}, [initialUser]);
return initialUser;
}
Cómo leer el estado de autenticación en el servidor
Usaremos FirebaseServerApp para replicar el estado de autenticación del cliente en el servidor.
Abre src/lib/firebase/serverApp.js
y reemplaza la función getAuthenticatedAppForUser
:
export async function getAuthenticatedAppForUser() {
const authIdToken = (await cookies()).get("__session")?.value;
// Firebase Server App is a new feature in the JS SDK that allows you to
// instantiate the SDK with credentials retrieved from the client & has
// other affordances for use in server environments.
const firebaseServerApp = initializeServerApp(
// https://github.com/firebase/firebase-js-sdk/issues/8863#issuecomment-2751401913
initializeApp(),
{
authIdToken,
}
);
const auth = getAuth(firebaseServerApp);
await auth.authStateReady();
return { firebaseServerApp, currentUser: auth.currentUser };
}
Verifica los cambios
El diseño raíz en el archivo src/app/layout.js
renderiza el encabezado y pasa el usuario, si está disponible, como una prop.
<Header initialUser={currentUser?.toJSON()} />
Esto significa que el componente <Header>
renderiza los 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, el controlador onAuthStateChanged
las controla.
Ahora es momento de lanzar una nueva compilación y verificar lo que compilaste.
- Crea una confirmación con el mensaje "Add authentication" y envíala a tu repositorio de GitHub.
- Abre la página App Hosting en Firebase console y espera a que se complete el nuevo lanzamiento.
- Verifica el nuevo comportamiento de autenticación:
- En tu navegador, actualiza la app web. Tu nombre visible aparecerá en el encabezado.
- Sal y vuelve a acceder. Puedes repetir este paso con diferentes usuarios.
- Opcional: Haz clic con el botón derecho en la app web, selecciona Ver código fuente de la 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 simulados para 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:
- Si aún no lo hiciste, accede a la app web. Luego, selecciona
> Agregar restaurantes de muestra.
- En la página Base de datos de Firestore de Firebase console, selecciona restaurantes. Verás los documentos de nivel superior en la colección de restaurantes, cada uno de los cuales representa un restaurante.
- 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:
- En el archivo
src/app/page.js
, busca el componente del servidor<Home />
y revisa la llamada a la funcióngetRestaurants
, que recupera una lista de restaurantes en el tiempo de ejecución del servidor. Implementarás la funcióngetRestaurants
en los siguientes pasos. - En el archivo
src/lib/firebase/firestore.js
, reemplaza las funcionesapplyQueryFilters
ygetRestaurants
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(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(),
};
});
}
- Crea una confirmación con el mensaje "Lee la lista de restaurantes de Firestore" y envíala a tu repositorio de GitHub.
- Abre la página App Hosting en Firebase console y espera a que se complete el nuevo lanzamiento.
- En la app web, actualiza la página. Las imágenes de los restaurantes aparecen como mosaicos en la página.
Verifica que las fichas de restaurantes se carguen durante el tiempo de ejecución del servidor
Con el framework de Next.js, es posible que no sea obvio cuándo se cargan los datos en el tiempo de ejecución del servidor o en el tiempo de ejecución del cliente.
Para verificar que las fichas de restaurantes se carguen durante el tiempo de ejecución del servidor, sigue estos pasos:
- En la app web, abre Herramientas para desarrolladores y deshabilita JavaScript.
- Actualiza la app web. Las fichas de restaurantes se siguen cargando. La información del restaurante se devuelve 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.
- 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 se cargó el conjunto inicial de restaurantes desde el archivo src/app/page.js
. El archivo src/app/page.js
es un componente del 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.
Para configurar el archivo src/components/RestaurantListings.jsx
de modo que hidrate el lenguaje de marcado renderizado por el servidor, sigue estos pasos:
- En el archivo
src/components/RestaurantListings.jsx
, observa el siguiente código, que ya está escrito para ti:
useEffect(() => {
return getRestaurantsSnapshot((data) => {
setRestaurants(data);
}, filters);
}, [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 instantánea 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.
- En el archivo
src/lib/firebase/firestore.js
, reemplaza la funcióngetRestaurantsSnapshot()
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);
return onSnapshot(q, (querySnapshot) => {
const results = querySnapshot.docs.map((doc) => {
return {
id: doc.id,
...doc.data(),
// Only plain objects can be passed to Client Components from Server Components
timestamp: doc.data().timestamp.toDate(),
};
});
cb(results);
});
}
Los cambios realizados a través de la página Firestore Database ahora se reflejan en la app web en tiempo real.
- Crea una confirmación con el mensaje "Listen for realtime restaurant updates" y envíala a tu repositorio de GitHub.
- Abre la página App Hosting en Firebase console y espera a que se complete el nuevo lanzamiento.
- En la app web, selecciona
> Add sample restaurants. Si la función de instantánea se implementó correctamente, los restaurantes aparecerán en tiempo real sin que se actualice la página.
8. Guarda las opiniones enviadas por los usuarios desde la app web
- En el archivo
src/lib/firebase/firestore.js
, reemplaza la funciónupdateWithRating()
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()),
});
};
Este código inserta un documento nuevo de Firestore que representa la nueva revisión. El código también actualiza el documento de Firestore existente que representa al restaurante con cifras actualizadas para la cantidad de calificaciones y la calificación promedio calculada.
- 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 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;
}
}
Implementa una acción del servidor de Next.js
Una acción del servidor de Next.js proporciona una API conveniente para acceder a los datos del formulario, como data.get("text")
para obtener el valor de texto de la carga útil del envío del formulario.
Para usar una acción del servidor de Next.js para procesar el envío del formulario de revisión, sigue estos pasos:
- En el archivo
src/components/ReviewDialog.jsx
, busca el atributoaction
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.
- En el archivo
src/app/actions.js
, reemplaza la funciónhandleReviewFormSubmission()
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:
- Crea una confirmación con el mensaje "Permite a los usuarios enviar opiniones sobre restaurantes" y envíala a tu repositorio de GitHub.
- Abre la página App Hosting en Firebase console y espera a que se complete el nuevo lanzamiento.
- Actualiza la app web y selecciona un restaurante en la página principal.
- En la página del restaurante, haz clic en
.
- Selecciona una calificación por estrellas.
- Escribe una opinión
- Haz clic en Enviar. Tu opinión aparecerá en la parte superior de la lista de opiniones.
- En Cloud Firestore, busca en el panel Agregar documento el documento del restaurante que revisaste y selecciónalo.
- En el panel Iniciar colección, selecciona calificaciones.
- En el panel Agregar documento, busca el documento de tu revisión para verificar que se haya insertado según lo esperado.
9. Guarda los archivos subidos por los usuarios desde la app web
En esta sección, agregarás funcionalidad para que puedas reemplazar la imagen asociada a un restaurante cuando accedas a tu cuenta. 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 app web, sigue estos pasos:
- 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);
setRestaurantDetails({ ...restaurantDetails, photo: imageURL });
}
No es necesario realizar ningún cambio en esta función, pero implementarás el comportamiento de la función updateRestaurantImage()
en los siguientes pasos.
- En el archivo
src/lib/firebase/storage.js
, reemplaza las funcionesupdateRestaurantImage()
yuploadImage()
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 funcionalidad de carga de imágenes
Para verificar que la imagen se suba según lo previsto, sigue estos pasos:
- Crea una confirmación con el mensaje "Permitir que los usuarios cambien la foto de cada restaurante" y envíala a tu repositorio de GitHub.
- Abre la página App Hosting en Firebase console y espera a que se complete el nuevo lanzamiento.
- En la app web, verifica que accediste y selecciona un restaurante.
- Haz clic en
y sube una imagen desde tu sistema de archivos. Tu imagen sale de tu entorno local y se sube a Cloud Storage. La imagen aparecerá inmediatamente después de que la subas.
- Navega a Cloud Storage para Firebase.
- Navega a la carpeta que representa el restaurante. La imagen que subiste existe en la carpeta.
10. Resume opiniones sobre restaurantes con IA generativa
En esta sección, agregarás una función de resumen de opiniones para que los usuarios puedan comprender rápidamente lo que todos piensan de un restaurante sin tener que leer cada opinión.
Almacena una clave de la API de Gemini en Cloud Secret Manager
- Para usar la API de Gemini, necesitarás una clave de API. Visita Google AI Studio y haz clic en "Crear clave de API".
- En la entrada "Search Google Cloud projects", elige tu proyecto de Firebase. Todos los proyectos de Firebase están respaldados por un proyecto de Google Cloud.
- App Hosting se integra en Cloud Secret Manager para permitirte almacenar de forma segura valores sensibles, como claves de API:
- En una terminal, ejecuta el comando para crear un secreto nuevo:
firebase apphosting:secrets:set GEMINI_API_KEY
- Cuando se te solicite el valor secreto, copia y pega tu clave de API de Gemini desde Google AI Studio.
- Cuando se te pregunte si el nuevo secreto es para pruebas locales o de producción, elige "Producción".
- Cuando se te pregunte si quieres otorgar acceso para que la cuenta de servicio de tu backend pueda acceder al secreto, selecciona “Sí”.
- Cuando se te pregunte si se debe agregar el secreto nuevo a
apphosting.yaml
, ingresaY
para aceptar.
Tu clave de la API de Gemini ahora se almacena de forma segura en Cloud Secret Manager y es accesible para tu backend de App Hosting.
Implementa el componente de resumen de opiniones
- En
src/components/Reviews/ReviewSummary.jsx
, reemplaza la funciónGeminiSummary
por el siguiente código:export async function GeminiSummary({ restaurantId }) { const { firebaseServerApp } = await getAuthenticatedAppForUser(); const reviews = await getReviewsByRestaurantId( getFirestore(firebaseServerApp), restaurantId ); const reviewSeparator = "@"; const prompt = ` Based on the following restaurant reviews, where each review is separated by a '${reviewSeparator}' character, create a one-sentence summary of what people think of the restaurant. Here are the reviews: ${reviews.map((review) => review.text).join(reviewSeparator)} `; try { if (!process.env.GEMINI_API_KEY) { // Make sure GEMINI_API_KEY environment variable is set: // https://firebase.google.com/docs/genkit/get-started throw new Error( 'GEMINI_API_KEY not set. Set it with "firebase apphosting:secrets:set GEMINI_API_KEY"' ); } // Configure a Genkit instance. const ai = genkit({ plugins: [googleAI()], model: gemini20Flash, // set default model }); const { text } = await ai.generate(prompt); return ( <div className="restaurant__review_summary"> <p>{text}</p> <p>✨ Summarized with Gemini</p> </div> ); } catch (e) { console.error(e); return <p>Error summarizing reviews.</p>; } }
- Crea una confirmación con el mensaje "Use AI to summarize reviews" y envíala a tu repositorio de GitHub.
- Abre la página App Hosting en Firebase console y espera a que se complete el nuevo lanzamiento.
- Abre una página de un restaurante. En la parte superior, deberías ver un resumen de una oración de todas las opiniones de la página.
- Agrega una opinión nueva y actualiza la página. Deberías ver el cambio en el resumen.
11. Conclusión
¡Felicitaciones! Aprendiste a usar Firebase para agregar funciones y funcionalidades a una app de Next.js. Específicamente, usaste lo siguiente:
- Firebase App Hosting para compilar e implementar automáticamente tu código de Next.js cada vez que envíes a una rama configurada
- Firebase Authentication para habilitar la funcionalidad de acceso y cierre de sesión
- Cloud Firestore para datos de restaurantes y opiniones sobre restaurantes
- Usa Cloud Storage para Firebase para las imágenes de restaurantes.