Laboratorio de código web de Cloud Firestore

1. Información general

Metas

En este laboratorio de programación, construirá una aplicación web recomendación de restaurante impulsado por la nube 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 laboratorio de código, asegúrese de haber instalado:

2. Crea y configura un proyecto de Firebase

Crea un proyecto de Firebase

  1. En la consola de Firebase , haga clic en Agregar proyecto, a continuación, el nombre de los proyectos FriendlyEats base de fuego.

Recuerde el ID del proyecto de su proyecto de Firebase.

  1. Haga clic en Crear proyecto.

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

  • Autenticación de base de fuego para identificar fácilmente los usuarios
  • Nube Firestore para guardar datos estructurados en la nube y obtener una notificación instantánea cuando se actualizan los datos
  • Firebase hosting para alojar y servir a 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 la consola de Firebase.

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. Vamos a utilizar inicio de sesión Anónimo - lo que significa que el usuario se firmó en silencio y sin que se le pida.

Usted necesita habilitar inicio de sesión anónimo.

  1. En la consola Firebase, busque la sección Generar en el panel de navegación izquierdo.
  2. Haga clic en Autenticación, a continuación, haga clic en la pestaña Inicio de sesión método (o haga clic aquí para ir directamente allí).
  3. Habilitar el inicio de sesión en el proveedor de Anónimo, a continuación, 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 aprender más.

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 de construcción de la consola Firebase, haga clic Firestore base de datos. Haga clic en Crear base de datos en el panel de la nube 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 ficha Reglas de la consola Firebase añadir las siguientes reglas y luego haga 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.

3. Obtén el código de muestra

Clonar el repositorio GitHub desde la línea de comandos:

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

El código de ejemplo debería haber sido clonado en el 📁 friendlyeats-web directorio. A partir de ahora, asegúrese de ejecutar todos sus comandos desde este directorio:

cd friendlyeats-web

Importar la aplicación de inicio

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

4. Instale la interfaz de línea de comandos de Firebase

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 comando 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 indique, seleccione su identificación del proyecto, a continuación, dar a su proyecto Firebase un alias.

Un alias es útil si tiene varios entornos (producción, preparación, etc.). Sin embargo, para este laboratorio de programación, vamos a utilizar el alias de default .

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

5. Ejecute el servidor local

¡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 utilizando el alojamiento Firebase emulador para servir a nuestra aplicación localmente. La aplicación web debe 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

6. Escribe datos en Cloud Firestore

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 de forma manual a través de la consola de Firebase , pero lo haremos en la propia aplicación para mostrar una escritura básica Nube Firestore.

Modelo de datos

Los datos de Firestore se dividen en colecciones, documentos, campos y subcolecciones. Vamos a almacenar cada restaurante como un documento en una colección de nivel superior llamado restaurants .

img3.png

Más tarde, vamos a almacenar cada examen en una subcolección llamada ratings correspondientes a cada restaurante.

img4.png

Agregar restaurantes a Firestore

El objeto principal del modelo en nuestra aplicación es un restaurante. Vamos a escribir algo de código que añade un documento restaurante para el restaurants colección.

  1. A partir de los archivos descargados, abierto scripts/FriendlyEats.Data.js .
  2. Encuentra 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 añade un nuevo documento en el restaurants colección. Los datos del documento provienen de un objeto JavaScript simple. Hacemos esto al obtener primero una referencia a una colección Nube FireStore restaurants continuación, add 'ing los datos.

¡Agreguemos restaurantes!

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

La aplicación generará automáticamente un conjunto aleatorio de objetos restaurantes, a continuación, llame a su addRestaurant función. Sin embargo, aún no verá los datos en su aplicación web real, porque todavía tenemos que poner en práctica la recuperación de los datos (la siguiente sección del laboratorio de programación).

Si se desplaza a la pestaña de la nube Firestore en la consola Firebase, sin embargo, ahora debería ver nuevos documentos en el restaurants colección!

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.

7. Mostrar datos de Cloud Firestore

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. Volver a los archivos de scripts/FriendlyEats.Data.js .
  2. Encontrar 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, se construye una consulta que recuperará hasta 50 restaurantes de la colección de nivel superior denominada restaurants , que están ordenados por la media de puntuación (actualmente todos cero). Después de que declaramos esta consulta, se pasa a la getDocumentsInQuery() método que es responsable de la carga y de la representación de los datos.

Haremos esto agregando un oyente de instantáneas.

  1. Volver a los archivos de scripts/FriendlyEats.Data.js .
  2. Encuentra 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 disparará su devolución de llamada cada vez que hay 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, el conjunto restaurants de recogida de la nube Firestore. A continuación, pasa todos los documentos individuales a la renderer.display función.
  • Cuando se elimina un documento, change.type es igual 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

8. Obtener () datos

Hasta ahora, hemos demostrado cómo utilizar 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. Volver a su archivo de scripts/FriendlyEats.Data.js .
  2. Encuentra 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 las 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.

9. Ordenar y filtrar datos

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.

He aquí un ejemplo de una consulta sencilla para recuperar todos los Dim Sum restaurantes:

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

Como su nombre lo indica, el where() método hará que nuestra consulta descarga sólo los miembros de la colección cuyos campos cumplir con las restricciones que nos propusimos. En este caso, sólo se descargará restaurantes donde category es Dim Sum .

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

Crearemos un método que genera una consulta que filtrará nuestros restaurantes en función de múltiples criterios seleccionados por nuestros usuarios.

  1. Volver a su archivo de scripts/FriendlyEats.Data.js .
  2. Encontrar 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 añade múltiples where los filtros y un solo orderBy cláusula para construir una consulta compuesto a base de 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 la consola de Firebase con los parámetros correctos completados. En la siguiente sección, escribiremos e implementaremos los índices necesarios para esta aplicación.

10. Implementar índices

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 descargado directorio local de su aplicación, encontrará una firestore.indexes.json archivo.

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.

11. Escribe datos en una transacció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 un error, 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 necesitaremos coordinar varias lecturas y escrituras. En primer lugar la revisión en sí tiene que ser sometida, a continuación, del restaurante calificación count y la average rating necesidad de ser actualizados. Si uno de estos falla pero no el otro, nos 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, asegurando que nuestros datos permanezcan consistentes.

  1. Volver a su archivo de scripts/FriendlyEats.Data.js .
  2. Encuentra 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, se activan una transacción para actualizar los valores numéricos de avgRating y numRatings en el documento restaurante. Al mismo tiempo, se añade la nueva rating a la ratings subcolección.

12. Asegure sus datos

Al comienzo de este laboratorio de código, 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 de construcción de la consola Firebase, haga clic Firestore base de datos.
  2. Haga clic en la ficha Reglas de la sección Nube Firestore (o haga clic aquí para ir directamente allí).
  3. Reemplazar los valores predeterminados con las siguientes reglas, a continuación, 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 o cualquier 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 firestore.rules archivo en el directorio de trabajo ya contiene las reglas desde 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

13. Conclusión

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

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