Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.
Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Laboratorio de código web de Cloud Firestore

Metas

En este laboratorio de código, creará una aplicación web de recomendación de restaurantes con tecnología de Cloud Firestore .

img5.png

Lo que aprenderás

  • Leer y escribir datos en Cloud Firestore desde una aplicación web
  • Escuche los cambios en los datos de Cloud Firestore en tiempo real
  • Use Firebase Authentication y las reglas de seguridad para proteger los datos de Cloud Firestore
  • Escribe consultas complejas de Cloud Firestore

Lo que necesitarás

Antes de iniciar este codelab, asegúrese de haber instalado:

Crea un proyecto de Firebase

  1. En Firebase console , haz clic en Agregar proyecto y , luego, asigna el nombre FriendlyEats al proyecto de Firebase.

Recuerde el ID del proyecto de su proyecto de Firebase.

  1. Haz clic en Crear proyecto .

La aplicación que vamos a crear utiliza algunos servicios de Firebase disponibles en la web:

  • Autenticación de Firebase para identificar fácilmente a sus usuarios
  • Cloud Firestore para guardar datos estructurados en la nube y recibir notificaciones instantáneas cuando se actualizan los datos
  • Firebase Hosting para alojar y entregar sus activos estáticos

Para este laboratorio de código específico, ya configuramos Firebase Hosting. Sin embargo, para Firebase Auth y Cloud Firestore, lo guiaremos a través de la configuración y habilitación de los servicios usando Firebase console.

Habilitar autenticación anónima

Aunque la autenticación no es el foco de este laboratorio de código, es importante tener alguna forma de autenticación en nuestra aplicación. Usaremos el inicio de sesión anónimo , lo que significa que el usuario iniciará sesión silenciosamente sin que se le solicite.

Deberá habilitar el inicio de sesión anónimo.

  1. En Firebase console, ubique la sección Desarrollar en el panel de navegación izquierdo.
  2. Haga clic en Autenticación y luego en la pestaña Método de inicio de sesión (o haga clic aquí para ir directamente allí).
  3. Habilite el Proveedor de inicio de sesión anónimo y luego haga clic en Guardar .

img7.png

Esto permitirá que la aplicación inicie sesión silenciosamente a sus usuarios cuando accedan a la aplicación web. No dude en leer la documentación de Autenticación anónima para obtener más información.

Habilitar Cloud Firestore

La aplicación usa Cloud Firestore para guardar y recibir información y calificaciones de restaurantes.

Deberá habilitar Cloud Firestore. En la sección Desarrollar de Firebase console, haz clic en Firestore . Haz clic en Crear base de datos en el panel de Cloud Firestore.

El acceso a los datos en Cloud Firestore está controlado por reglas de seguridad. Hablaremos más sobre las reglas más adelante en este laboratorio de código, pero primero debemos establecer algunas reglas básicas en nuestros datos para comenzar. En la pestaña Reglas de Firebase console, agrega las siguientes reglas y luego haz clic en Publicar .

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      //
      // WARNING: These rules are insecure! We will replace them with
      // more secure rules later in the codelab
      //
      allow read, write: if request.auth != null;
    }
  }
}

Las reglas anteriores restringen el acceso a los datos a los usuarios que han iniciado sesión, lo que evita que los usuarios no autenticados lean o escriban. Esto es mejor que permitir el acceso público, pero aún está lejos de ser seguro, mejoraremos estas reglas más adelante en el laboratorio de código.

Clona el repositorio de GitHub desde la línea de comando:

git clone https://github.com/firebase/friendlyeats-web

El código de muestra debería haber sido clonado en el directorio 📁 friendlyeats-web , asegúrese de que su línea de comando se ejecute desde este directorio de ahora en adelante:

cd friendlyeats-web

Importar la aplicación de inicio

Usando su IDE (WebStorm, Atom, Sublime, Visual Studio Code ...) abra o importe el directorio 📁 friendlyeats-web . Este directorio contiene el código de inicio para el laboratorio de código que consiste en una aplicación de recomendación de restaurantes que aún no funciona. Lo haremos funcional en todo este laboratorio de código, por lo que pronto necesitará editar el código en ese directorio.

La interfaz de línea de comandos de Firebase (CLI) te permite entregar tu aplicación web de forma local e implementarla en Firebase Hosting.

  1. Instale la CLI ejecutando el siguiente comando npm:
npm -g install firebase-tools
  1. Verifique que la CLI se haya instalado correctamente ejecutando el siguiente comando:
firebase --version

Asegúrese de que la versión de Firebase CLI sea v7.4.0 o posterior.

  1. Autorice Firebase CLI ejecutando el siguiente comando:
firebase login

Hemos configurado la plantilla de la aplicación web para extraer la configuración de su aplicación para Firebase Hosting del directorio y los archivos locales de su aplicación. Pero para hacer esto, necesitamos asociar su aplicación con su proyecto de Firebase.

  1. Asegúrese de que su línea de comandos acceda al directorio local de su aplicación.
  2. Asocie su aplicación con su proyecto de Firebase ejecutando el siguiente comando:
firebase use --add
  1. Cuando se le solicite, seleccione su ID de proyecto , luego asigne un alias a su proyecto de Firebase.

Un alias es útil si tiene varios entornos (producción, ensayo, etc.). Sin embargo, para este laboratorio de código, usemos el alias default .

  1. Siga las instrucciones restantes en su línea de comando.

¡Estamos listos para comenzar a trabajar en nuestra aplicación! ¡Ejecutemos nuestra aplicación localmente!

  1. Ejecute el siguiente comando de Firebase CLI:
firebase emulators:start --only hosting
  1. Su línea de comando debería mostrar la siguiente respuesta:
hosting: Local server: http://localhost:5000

Estamos usando el emulador de Firebase Hosting para entregar nuestra aplicación localmente. La aplicación web ahora debería estar disponible en http: // localhost: 5000 .

  1. Abra su aplicación en http: // localhost: 5000 .

Debería ver su copia de FriendlyEats que se ha conectado a su proyecto de Firebase.

La aplicación se ha conectado automáticamente a su proyecto de Firebase y ha iniciado sesión silenciosamente como usuario anónimo.

img2.png

En esta sección, escribiremos algunos datos en Cloud Firestore para que podamos completar la interfaz de usuario de la aplicación. Esto se puede hacer manualmente a través de la consola de Firebase , pero lo haremos en la propia aplicación para demostrar una escritura básica de Cloud Firestore.

Modelo de datos

Los datos de Firestore se dividen en colecciones, documentos, campos y subcolecciones. Almacenaremos cada restaurante como un documento en una colección de alto nivel llamada restaurants .

img3.png

Más adelante, almacenaremos cada reseña en una subcolección llamada ratings en cada restaurante.

img4.png

Agregar restaurantes a Firestore

El objeto modelo principal de nuestra aplicación es un restaurante. Escribamos un código que agregue un documento de restaurante a la colección de restaurants .

  1. Desde sus archivos descargados, abra scripts/FriendlyEats.Data.js .
  2. Busque la función FriendlyEats.prototype.addRestaurant .
  3. Reemplace toda la función con el siguiente código.

FriendlyEats.Data.js

FriendlyEats.prototype.addRestaurant = function(data) {
  var collection = firebase.firestore().collection('restaurants');
  return collection.add(data);
};

El código anterior agrega un nuevo documento a la colección de restaurants . Los datos del documento provienen de un objeto JavaScript simple. Hacemos esto obteniendo primero una referencia a una colección de restaurants Cloud Firestore restaurants luego add los datos.

¡Agreguemos restaurantes!

  1. Vuelva a su aplicación FriendlyEats en su navegador y actualícela.
  2. Haga clic en Agregar datos simulados .

La aplicación generará automáticamente un conjunto aleatorio de objetos de restaurantes y luego llamará a la función addRestaurant . Sin embargo, todavía no verá los datos en su aplicación web real porque todavía necesitamos implementar la recuperación de los datos (la siguiente sección del laboratorio de código).

Sin embargo, si navega a la pestaña Cloud Firestore en Firebase console, ¡ahora debería ver nuevos documentos en la colección de restaurants !

img6.png

¡Felicitaciones, acaba de escribir datos en Cloud Firestore desde una aplicación web!

En la siguiente sección, aprenderá cómo recuperar datos de Cloud Firestore y mostrarlos en su aplicación.

En esta sección, aprenderá cómo recuperar datos de Cloud Firestore y mostrarlos en su aplicación. Los dos pasos clave son crear una consulta y agregar un oyente de instantáneas. Este oyente será notificado de todos los datos existentes que coincidan con la consulta y recibirá actualizaciones en tiempo real.

Primero, construyamos la consulta que servirá a la lista de restaurantes predeterminada y sin filtrar.

  1. Vuelva al archivo scripts/FriendlyEats.Data.js .
  2. Busque la función FriendlyEats.prototype.getAllRestaurants .
  3. Reemplace toda la función con el siguiente código.

FriendlyEats.Data.js

FriendlyEats.prototype.getAllRestaurants = function(renderer) {
  var query = firebase.firestore()
      .collection('restaurants')
      .orderBy('avgRating', 'desc')
      .limit(50);

  this.getDocumentsInQuery(query, renderer);
};

En el código anterior, construimos una consulta que recuperará hasta 50 restaurantes de la colección de nivel superior denominada restaurants , que están ordenados por la calificación promedio (actualmente todos cero). Después de que getDocumentsInQuery() esta consulta, la pasamos al método getDocumentsInQuery() que es responsable de cargar y representar los datos.

Haremos esto agregando un oyente de instantáneas.

  1. Vuelva al archivo scripts/FriendlyEats.Data.js .
  2. Busque la función FriendlyEats.prototype.getDocumentsInQuery .
  3. Reemplace toda la función con el siguiente código.

FriendlyEats.Data.js

FriendlyEats.prototype.getDocumentsInQuery = function(query, renderer) {
  query.onSnapshot(function(snapshot) {
    if (!snapshot.size) return renderer.empty(); // Display "There are no restaurants".

    snapshot.docChanges().forEach(function(change) {
      if (change.type === 'removed') {
        renderer.remove(change.doc);
      } else {
        renderer.display(change.doc);
      }
    });
  });
};

En el código anterior, query.onSnapshot activará su devolución de llamada cada vez que haya un cambio en el resultado de la consulta.

  • La primera vez, la devolución de llamada se activa con todo el conjunto de resultados de la consulta, es decir, toda la colección de restaurants de Cloud Firestore. Luego pasa todos los documentos individuales a la función renderer.display .
  • Cuando se elimina un documento, change.type equivale a removed . Entonces, en este caso, llamaremos a una función que elimina el restaurante de la interfaz de usuario.

Ahora que hemos implementado ambos métodos, actualice la aplicación y verifique que los restaurantes que vimos anteriormente en la consola de Firebase ahora estén visibles en la aplicación. Si completó esta sección con éxito, entonces su aplicación ahora está leyendo y escribiendo datos con Cloud Firestore.

A medida que cambia su lista de restaurantes, este oyente se actualizará automáticamente. Intente ir a la consola de Firebase y eliminar manualmente un restaurante o cambiar su nombre; verá que los cambios aparecen en su sitio de inmediato.

img5.png

Hasta ahora, hemos mostrado cómo usar onSnapshot para recuperar actualizaciones en tiempo real; sin embargo, eso no siempre es lo que queremos. A veces tiene más sentido buscar los datos una sola vez.

Queremos implementar un método que se activa cuando un usuario hace clic en un restaurante específico en su aplicación.

  1. Vuelva a sus scripts/FriendlyEats.Data.js archivo scripts/FriendlyEats.Data.js .
  2. Busque la función FriendlyEats.prototype.getRestaurant .
  3. Reemplace toda la función con el siguiente código.

FriendlyEats.Data.js

FriendlyEats.prototype.getRestaurant = function(id) {
  return firebase.firestore().collection('restaurants').doc(id).get();
};

Una vez que haya implementado este método, podrá ver páginas de cada restaurante. Simplemente haga clic en un restaurante de la lista y debería ver la página de detalles del restaurante:

img1.png

Por ahora, no puede agregar calificaciones, ya que aún necesitamos implementar la adición de calificaciones más adelante en el laboratorio de código.

Actualmente, nuestra aplicación muestra una lista de restaurantes, pero el usuario no tiene forma de filtrar según sus necesidades. En esta sección, usará las consultas avanzadas de Cloud Firestore para habilitar el filtrado.

Aquí hay un ejemplo de una consulta simple para buscar todos los restaurantes Dim Sum :

var filteredQuery = query.where('category', '==', 'Dim Sum')

Como su nombre lo indica, el método where() hará que nuestra consulta descargue solo miembros de la colección cuyos campos cumplan con las restricciones que establecemos. En este caso, solo descargará restaurantes cuya category sea Dim Sum .

En nuestra aplicación, el usuario puede encadenar múltiples filtros para crear consultas específicas, como "Pizza en San Francisco" o "Mariscos en Los Ángeles ordenados por popularidad".

Crearemos un método que construye una consulta que filtrará nuestros restaurantes en base a múltiples criterios seleccionados por nuestros usuarios.

  1. Vuelva a sus scripts/FriendlyEats.Data.js archivo scripts/FriendlyEats.Data.js .
  2. Busque la función FriendlyEats.prototype.getFilteredRestaurants .
  3. Reemplace toda la función con el siguiente código.

FriendlyEats.Data.js

FriendlyEats.prototype.getFilteredRestaurants = function(filters, renderer) {
  var query = firebase.firestore().collection('restaurants');

  if (filters.category !== 'Any') {
    query = query.where('category', '==', filters.category);
  }

  if (filters.city !== 'Any') {
    query = query.where('city', '==', filters.city);
  }

  if (filters.price !== 'Any') {
    query = query.where('price', '==', filters.price.length);
  }

  if (filters.sort === 'Rating') {
    query = query.orderBy('avgRating', 'desc');
  } else if (filters.sort === 'Reviews') {
    query = query.orderBy('numRatings', 'desc');
  }

  this.getDocumentsInQuery(query, renderer);
};

El código anterior agrega varios filtros where y una sola cláusula orderBy para crear una consulta compuesta basada en la entrada del usuario. Nuestra consulta ahora solo devolverá restaurantes que coincidan con los requisitos del usuario.

Actualice su aplicación FriendlyEats en su navegador, luego verifique que puede filtrar por precio, ciudad y categoría. Durante la prueba, verá errores en la consola de JavaScript de su navegador que se ven así:

The query requires an index. You can create it here: https://console.firebase.google.com/project/.../database/firestore/indexes?create_index=...

Estos errores se deben a que Cloud Firestore requiere índices para la mayoría de las consultas compuestas. Requerir índices en las consultas mantiene a Cloud Firestore rápido a escala.

Al abrir el enlace del mensaje de error, se abrirá automáticamente la IU de creación de índices en Firebase console con los parámetros correctos completados. En la siguiente sección, escribiremos e implementaremos los índices necesarios para esta aplicación.

Si no queremos explorar todas las rutas en nuestra aplicación y seguir cada uno de los enlaces de creación de índices, podemos implementar fácilmente muchos índices a la vez usando Firebase CLI.

  1. En el directorio local descargado de su aplicación, encontrará un archivo firestore.indexes.json .

Este archivo describe todos los índices necesarios para todas las posibles combinaciones de filtros.

firestore.indexes.json

{
 "indexes": [
   {
     "collectionGroup": "restaurants",
     "queryScope": "COLLECTION",
     "fields": [
       { "fieldPath": "city", "order": "ASCENDING" },
       { "fieldPath": "avgRating", "order": "DESCENDING" }
     ]
   },

   ...

 ]
}
  1. Implemente estos índices con el siguiente comando:
firebase deploy --only firestore:indexes

Después de unos minutos, sus índices estarán activos y los mensajes de error desaparecerán.

En esta sección, agregaremos la posibilidad de que los usuarios envíen reseñas a los restaurantes. Hasta ahora, todas nuestras escrituras han sido atómicas y relativamente simples. Si alguno de ellos tiene errores, es probable que le pidamos al usuario que vuelva a intentarlo o nuestra aplicación volverá a intentar la escritura automáticamente.

Nuestra aplicación tendrá muchos usuarios que deseen agregar una calificación para un restaurante, por lo que tendremos que coordinar varias lecturas y escrituras. Primero se debe enviar la reseña en sí, luego se debe actualizar el count calificaciones del restaurante y la calificación average rating . Si uno de estos falla pero no el otro, quedamos en un estado inconsistente en el que los datos de una parte de nuestra base de datos no coinciden con los de otra.

Afortunadamente, Cloud Firestore proporciona una funcionalidad de transacción que nos permite realizar múltiples lecturas y escrituras en una sola operación atómica, lo que garantiza que nuestros datos permanezcan consistentes.

  1. Vuelva a sus scripts/FriendlyEats.Data.js archivo scripts/FriendlyEats.Data.js .
  2. Busque la función FriendlyEats.prototype.addRating .
  3. Reemplace toda la función con el siguiente código.

FriendlyEats.Data.js

FriendlyEats.prototype.addRating = function(restaurantID, rating) {
  var collection = firebase.firestore().collection('restaurants');
  var document = collection.doc(restaurantID);
  var newRatingDocument = document.collection('ratings').doc();

  return firebase.firestore().runTransaction(function(transaction) {
    return transaction.get(document).then(function(doc) {
      var data = doc.data();

      var newAverage =
          (data.numRatings * data.avgRating + rating.rating) /
          (data.numRatings + 1);

      transaction.update(document, {
        numRatings: data.numRatings + 1,
        avgRating: newAverage
      });
      return transaction.set(newRatingDocument, rating);
    });
  });
};

En el bloque anterior, avgRating una transacción para actualizar los valores numéricos de avgRating y numRatings en el documento del restaurante. Al mismo tiempo, agregamos la nueva rating a la subcolección de ratings .

Al comienzo de este codelab, establecemos las reglas de seguridad de nuestra aplicación para abrir completamente la base de datos a cualquier lectura o escritura. En una aplicación real, nos gustaría establecer reglas mucho más detalladas para evitar el acceso o la modificación de datos no deseados.

  1. En la sección Desarrollar de Firebase console, haz clic en Base de datos .
  2. Haga clic en la pestaña Reglas en la sección Cloud Firestore (o haga clic aquí para ir directamente allí).
  3. Reemplace los valores predeterminados con las siguientes reglas y luego haga clic en Publicar .

firestore.rules

rules_version = '2';
service cloud.firestore {

  // Determine if the value of the field "key" is the same
  // before and after the request.
  function unchanged(key) {
    return (key in resource.data) 
      && (key in request.resource.data) 
      && (resource.data[key] == request.resource.data[key]);
  }

  match /databases/{database}/documents {
    // Restaurants:
    //   - Authenticated user can read
    //   - Authenticated user can create/update (for demo purposes only)
    //   - Updates are allowed if no fields are added and name is unchanged
    //   - Deletes are not allowed (default)
    match /restaurants/{restaurantId} {
      allow read: if request.auth != null;
      allow create: if request.auth != null;
      allow update: if request.auth != null
                    && (request.resource.data.keys() == resource.data.keys()) 
                    && unchanged("name");
      
      // Ratings:
      //   - Authenticated user can read
      //   - Authenticated user can create if userId matches
      //   - Deletes and updates are not allowed (default)
      match /ratings/{ratingId} {
        allow read: if request.auth != null;
        allow create: if request.auth != null
                      && request.resource.data.userId == request.auth.uid;
      }
    }
  }
}

Estas reglas restringen el acceso para garantizar que los clientes solo realicen cambios seguros. Por ejemplo:

  • Las actualizaciones de un documento de restaurante solo pueden cambiar las calificaciones, no el nombre ni ningún otro dato inmutable.
  • Las calificaciones solo se pueden crear si el ID de usuario coincide con el usuario que inició sesión, lo que evita la suplantación de identidad.

Como alternativa a usar Firebase console, puedes usar Firebase CLI para implementar reglas en tu proyecto de Firebase. El archivo firestore.rules en su directorio de trabajo ya contiene las reglas de arriba. Para implementar estas reglas desde su sistema de archivos local (en lugar de usar la consola de Firebase), debe ejecutar el siguiente comando:

firebase deploy --only firestore:rules

En este laboratorio de código, aprendió cómo realizar lecturas y escrituras básicas y avanzadas con Cloud Firestore, así como también cómo proteger el acceso a los datos con reglas de seguridad. Puede encontrar la solución completa en el repositorio quickstarts-js .

Para obtener más información sobre Cloud Firestore, visite los siguientes recursos: