Check out what’s new from Firebase@ Google I/O 2021, and join our alpha program for early access to the new Remote Config personalization feature. Learn more

Atelier de programmation Web Cloud Firestore

Buts

Dans cet atelier de programmation, vous allez créer une application Web de recommandation de restaurants optimisée par Cloud Firestore .

img5.png

Ce que vous apprendrez

  • Lire et écrire des données dans Cloud Firestore à partir d'une application Web
  • Écoutez les changements dans les données Cloud Firestore en temps réel
  • Utiliser l'authentification Firebase et les règles de sécurité pour sécuriser les données Cloud Firestore
  • Écrire des requêtes Cloud Firestore complexes

Ce dont vous aurez besoin

Avant de commencer cet atelier de programmation, assurez-vous d'avoir installé :

Créer un projet Firebase

  1. Dans la console Firebase , cliquez sur Ajouter un projet , puis nommez le projet Firebase FriendlyEats .

N'oubliez pas l'ID de projet de votre projet Firebase.

  1. Cliquez sur Créer un projet .

L'application que nous allons créer utilise quelques services Firebase disponibles sur le Web :

  • Authentification Firebase pour identifier facilement vos utilisateurs
  • Cloud Firestore pour enregistrer des données structurées sur le Cloud et recevoir une notification instantanée lorsque les données sont mises à jour
  • Firebase Hosting pour héberger et servir vos actifs statiques

Pour cet atelier de programmation spécifique, nous avons déjà configuré Firebase Hosting. Cependant, pour Firebase Auth et Cloud Firestore, nous vous expliquerons la configuration et l'activation des services à l'aide de la console Firebase.

Activer l'authentification anonyme

Bien que l'authentification ne soit pas l'objet de cet atelier de programmation, il est important d'avoir une certaine forme d'authentification dans notre application. Nous utiliserons la connexion anonyme - ce qui signifie que l'utilisateur sera connecté en silence sans y être invité.

Vous devrez activer la connexion anonyme.

  1. Dans la console Firebase, localisez la section Build dans la barre de navigation de gauche.
  2. Cliquez sur Authentification , puis sur l'onglet Méthode de connexion (ou cliquez ici pour y accéder directement).
  3. Activez le fournisseur de connexion anonyme , puis cliquez sur Enregistrer .

img7.png

Cela permettra à l'application de se connecter silencieusement à vos utilisateurs lorsqu'ils accèdent à l'application Web. N'hésitez pas à lire la documentation sur l' authentification anonyme pour en savoir plus.

Activer Cloud Firestore

L'application utilise Cloud Firestore pour enregistrer et recevoir des informations et des évaluations sur les restaurants.

Vous devez activer Cloud Firestore. Dans la section Build de la console Firebase, cliquez sur Firestore Database . Cliquez sur Créer une base de données dans le volet Cloud Firestore.

L'accès aux données dans Cloud Firestore est contrôlé par des règles de sécurité. Nous parlerons plus en détail des règles plus tard dans cet atelier de programmation, mais nous devons d'abord définir quelques règles de base sur nos données pour commencer. Dans l' onglet Règles de la console Firebase, ajoutez les règles suivantes, puis cliquez sur Publier .

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

Les règles ci-dessus limitent l'accès aux données aux utilisateurs connectés, ce qui empêche les utilisateurs non authentifiés de lire ou d'écrire. C'est mieux que d'autoriser l'accès public mais c'est encore loin d'être sécurisé, nous améliorerons ces règles plus tard dans le labo.

Clonez le dépôt GitHub à partir de la ligne de commande :

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

L'exemple de code doit avoir été cloné dans le répertoire 📁 friendlyeats-web . A partir de maintenant, assurez-vous d'exécuter toutes vos commandes depuis ce répertoire :

cd friendlyeats-web

Importer l'application de démarrage

A l'aide de votre IDE (WebStorm, Atom, Sublime, Visual Studio Code...) ouvrez ou importez le répertoire 📁 friendlyeats-web . Ce répertoire contient le code de démarrage du laboratoire de programmation qui consiste en une application de recommandation de restaurants pas encore fonctionnelle. Nous le rendrons fonctionnel tout au long de cet atelier de programmation, vous devrez donc bientôt modifier le code dans ce répertoire.

L'interface de ligne de commande (CLI) de Firebase vous permet de servir votre application Web localement et de déployer votre application Web sur Firebase Hosting.

  1. Installez la CLI en exécutant la commande npm suivante :
npm -g install firebase-tools
  1. Vérifiez que la CLI a été correctement installée en exécutant la commande suivante :
firebase --version

Assurez-vous que la version de Firebase CLI est la v7.4.0 ou une version ultérieure.

  1. Autorisez la CLI Firebase en exécutant la commande suivante :
firebase login

Nous avons configuré le modèle d'application Web pour extraire la configuration de votre application pour l'hébergement Firebase à partir du répertoire et des fichiers locaux de votre application. Mais pour ce faire, nous devons associer votre application à votre projet Firebase.

  1. Assurez-vous que votre ligne de commande accède au répertoire local de votre application.
  2. Associez votre application à votre projet Firebase en exécutant la commande suivante :
firebase use --add
  1. Lorsque vous y êtes invité, sélectionnez votre ID de projet , puis attribuez un alias à votre projet Firebase.

Un alias est utile si vous avez plusieurs environnements (production, staging, etc.). Cependant, pour cet atelier de programmation, utilisons simplement l'alias de default .

  1. Suivez les instructions restantes dans votre ligne de commande.

Nous sommes prêts à commencer à travailler sur notre application ! Exécutons notre application localement !

  1. Exécutez la commande CLI Firebase suivante :
firebase emulators:start --only hosting
  1. Votre ligne de commande devrait afficher la réponse suivante :
hosting: Local server: http://localhost:5000

Nous utilisons l'émulateur Firebase Hosting pour servir notre application localement. L'application Web devrait maintenant être disponible à partir de http://localhost:5000 .

  1. Ouvrez votre application sur http://localhost:5000 .

Vous devriez voir votre copie de FriendlyEats qui a été connectée à votre projet Firebase.

L'application s'est automatiquement connectée à votre projet Firebase et vous a connecté silencieusement en tant qu'utilisateur anonyme.

img2.png

Dans cette section, nous allons écrire des données dans Cloud Firestore afin de pouvoir remplir l'interface utilisateur de l'application. Cela peut être fait manuellement via la console Firebase , mais nous le ferons dans l'application elle-même pour démontrer une écriture de base sur Cloud Firestore.

Modèle de données

Les données Firestore sont divisées en collections, documents, champs et sous-collections. Nous allons stocker chaque restaurant sous forme de document dans une collection de niveau supérieur appelée restaurants .

img3.png

Plus tard, nous stockerons chaque avis dans une sous-collection appelée ratings sous chaque restaurant.

img4.png

Ajouter des restaurants à Firestore

L'objet modèle principal de notre application est un restaurant. Écrivons un code qui ajoute un document de restaurant à la collection de restaurants .

  1. À partir de vos fichiers téléchargés, ouvrez scripts/FriendlyEats.Data.js .
  2. Trouvez la fonction FriendlyEats.prototype.addRestaurant .
  3. Remplacez la fonction entière par le code suivant.

FriendlyEats.Data.js

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

Le code ci-dessus ajoute un nouveau document à la collection de restaurants . Les données du document proviennent d'un objet JavaScript simple. Pour ce faire, nous obtenons d'abord une référence à une collection de restaurants Cloud Firestore restaurants puis nous add les données.

Ajoutons les restaurants !

  1. Retournez à votre application FriendlyEats dans votre navigateur et actualisez-la.
  2. Cliquez sur Ajouter des données fictives .

L'application générera automatiquement un ensemble aléatoire d'objets de restaurants, puis appellera votre fonction addRestaurant . Cependant, vous ne verrez pas encore les données dans votre application Web actuelle, car nous devons encore implémenter la récupération des données (la section suivante du laboratoire de programmation).

Si vous accédez à l' onglet Cloud Firestore dans la console Firebase, vous devriez maintenant voir de nouveaux documents dans la collection de restaurants !

img6.png

Félicitations, vous venez d'écrire des données dans Cloud Firestore à partir d'une application Web !

Dans la section suivante, vous apprendrez à récupérer des données depuis Cloud Firestore et à les afficher dans votre application.

Dans cette section, vous apprendrez à récupérer des données depuis Cloud Firestore et à les afficher dans votre application. Les deux étapes clés consistent à créer une requête et à ajouter un écouteur d'instantané. Cet écouteur sera informé de toutes les données existantes qui correspondent à la requête et recevra des mises à jour en temps réel.

Tout d'abord, construisons la requête qui servira la liste de restaurants par défaut et non filtrée.

  1. Revenez au fichier scripts/FriendlyEats.Data.js .
  2. Trouvez la fonction FriendlyEats.prototype.getAllRestaurants .
  3. Remplacez la fonction entière par le code suivant.

FriendlyEats.Data.js

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

  this.getDocumentsInQuery(query, renderer);
};

Dans le code ci-dessus, nous construisons une requête qui récupérera jusqu'à 50 restaurants de la collection de premier niveau nommée restaurants , qui sont classés par note moyenne (actuellement tous zéro). Après avoir déclaré cette requête, nous la transmettons à la méthode getDocumentsInQuery() qui est responsable du chargement et du rendu des données.

Nous allons le faire en ajoutant un écouteur d'instantané.

  1. Revenez au fichier scripts/FriendlyEats.Data.js .
  2. Recherchez la fonction FriendlyEats.prototype.getDocumentsInQuery .
  3. Remplacez la fonction entière par le code suivant.

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

Dans le code ci-dessus, query.onSnapshot déclenchera son rappel chaque fois qu'il y aura une modification du résultat de la requête.

  • La première fois, le rappel est déclenché avec l'ensemble des résultats de la requête, c'est-à-dire l'ensemble de la collection de restaurants de Cloud Firestore. Il transmet ensuite tous les documents individuels à la fonction renderer.display .
  • Lorsqu'un document est supprimé, change.type est égal à removed . Donc, dans ce cas, nous appellerons une fonction qui supprime le restaurant de l'interface utilisateur.

Maintenant que nous avons implémenté les deux méthodes, actualisez l'application et vérifiez que les restaurants que nous avons vus précédemment dans la console Firebase sont désormais visibles dans l'application. Si vous avez terminé cette section avec succès, votre application lit et écrit désormais des données avec Cloud Firestore !

Au fur et à mesure que votre liste de restaurants change, cet écouteur continuera à se mettre à jour automatiquement. Essayez d'accéder à la console Firebase et de supprimer manuellement un restaurant ou de changer son nom - vous verrez les changements apparaître immédiatement sur votre site !

img5.png

Jusqu'à présent, nous avons montré comment utiliser onSnapshot pour récupérer les mises à jour en temps réel ; cependant, ce n'est pas toujours ce que nous voulons. Parfois, il est plus logique de ne récupérer les données qu'une seule fois.

Nous souhaitons implémenter une méthode qui se déclenche lorsqu'un utilisateur clique sur un restaurant spécifique dans votre application.

  1. Retournez dans votre fichier scripts/FriendlyEats.Data.js .
  2. Trouvez la fonction FriendlyEats.prototype.getRestaurant .
  3. Remplacez la fonction entière par le code suivant.

FriendlyEats.Data.js

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

Après avoir implémenté cette méthode, vous pourrez afficher les pages de chaque restaurant. Cliquez simplement sur un restaurant dans la liste et vous devriez voir la page des détails du restaurant :

img1.png

Pour l'instant, vous ne pouvez pas ajouter de notes, car nous devons encore implémenter l'ajout de notes plus tard dans le laboratoire de programmation.

Actuellement, notre application affiche une liste de restaurants, mais il n'y a aucun moyen pour l'utilisateur de filtrer en fonction de ses besoins. Dans cette section, vous utiliserez les requêtes avancées de Cloud Firestore pour activer le filtrage.

Voici un exemple de requête simple pour récupérer tous les restaurants Dim Sum :

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

Comme son nom l'indique, la méthode where() fera que notre requête ne télécharge que les membres de la collection dont les champs répondent aux restrictions que nous avons définies. Dans ce cas, il ne téléchargera que les restaurants dont la category est Dim Sum .

Dans notre application, l'utilisateur peut enchaîner plusieurs filtres pour créer des requêtes spécifiques, comme "Pizza à San Francisco" ou "Fruits de mer à Los Angeles commandés par popularité".

Nous allons créer une méthode qui construit une requête qui filtrera nos restaurants en fonction de plusieurs critères sélectionnés par nos utilisateurs.

  1. Retournez dans votre fichier scripts/FriendlyEats.Data.js .
  2. Trouvez la fonction FriendlyEats.prototype.getFilteredRestaurants .
  3. Remplacez la fonction entière par le code suivant.

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

Le code ci-dessus ajoute plusieurs filtres where et une seule clause orderBy pour créer une requête composée basée sur l'entrée de l'utilisateur. Notre requête ne renverra désormais que les restaurants qui correspondent aux besoins de l'utilisateur.

Actualisez votre application FriendlyEats dans votre navigateur, puis vérifiez que vous pouvez filtrer par prix, ville et catégorie. Pendant le test, vous verrez des erreurs dans la console JavaScript de votre navigateur qui ressemblent à ceci :

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

Ces erreurs sont dues au fait que Cloud Firestore requiert des index pour la plupart des requêtes composées. Exiger des index sur les requêtes permet à Cloud Firestore de rester rapide à grande échelle.

L'ouverture du lien à partir du message d'erreur ouvrira automatiquement l'interface utilisateur de création d'index dans la console Firebase avec les paramètres corrects renseignés. Dans la section suivante, nous allons écrire et déployer les index nécessaires pour cette application.

Si nous ne voulons pas explorer tous les chemins de notre application et suivre chacun des liens de création d'index, nous pouvons facilement déployer plusieurs index à la fois à l'aide de la CLI Firebase.

  1. Dans le répertoire local téléchargé de votre application, vous trouverez un fichier firestore.indexes.json .

Ce fichier décrit tous les index nécessaires pour toutes les combinaisons possibles de filtres.

firestore.indexes.json

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

   ...

 ]
}
  1. Déployez ces index avec la commande suivante :
firebase deploy --only firestore:indexes

Après quelques minutes, vos index seront en ligne et les messages d'erreur disparaîtront.

Dans cette section, nous ajouterons la possibilité pour les utilisateurs de soumettre des avis aux restaurants. Jusqu'à présent, toutes nos écritures ont été atomiques et relativement simples. En cas d'erreur, nous inviterions probablement l'utilisateur à les réessayer ou notre application réessayerait l'écriture automatiquement.

Notre application aura de nombreux utilisateurs qui souhaitent ajouter une note pour un restaurant, nous devrons donc coordonner plusieurs lectures et écritures. Tout d' abord l'examen lui - même doit être soumis, puis note du restaurant count et average rating ont besoin d'être mis à jour. Si l'un d'entre eux échoue mais pas l'autre, nous nous retrouvons dans un état incohérent où les données d'une partie de notre base de données ne correspondent pas aux données d'une autre.

Heureusement, Cloud Firestore fournit une fonctionnalité de transaction qui nous permet d'effectuer plusieurs lectures et écritures en une seule opération atomique, garantissant ainsi la cohérence de nos données.

  1. Retournez dans votre fichier scripts/FriendlyEats.Data.js .
  2. Trouvez la fonction FriendlyEats.prototype.addRating .
  3. Remplacez la fonction entière par le code suivant.

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

Dans le bloc ci-dessus, nous déclenchons une transaction pour mettre à jour les valeurs numériques de avgRating et numRatings dans le document du restaurant. En même temps, nous ajoutons la nouvelle rating à la sous-collection des ratings .

Au début de cet atelier de programmation, nous avons défini les règles de sécurité de notre application pour ouvrir complètement la base de données à toute lecture ou écriture. Dans une application réelle, nous voudrions définir des règles beaucoup plus fines pour empêcher l'accès ou la modification de données indésirables.

  1. Dans la section Build de la console Firebase, cliquez sur Firestore Database .
  2. Cliquez sur l'onglet Règles dans la section Cloud Firestore (ou cliquez ici pour y accéder directement).
  3. Remplacez les valeurs par défaut par les règles suivantes, puis cliquez sur Publier .

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

Ces règles restreignent l'accès pour garantir que les clients n'effectuent que des modifications sûres. Par example:

  • Les mises à jour d'un document de restaurant ne peuvent modifier que les notes, pas le nom ou toute autre donnée immuable.
  • Les évaluations ne peuvent être créées que si l'ID utilisateur correspond à l'utilisateur connecté, ce qui empêche l'usurpation d'identité.

Au lieu d'utiliser la console Firebase, vous pouvez utiliser la CLI Firebase pour déployer des règles dans votre projet Firebase. Le fichier firestore.rules dans votre répertoire de travail contient déjà les règles ci-dessus. Pour déployer ces règles à partir de votre système de fichiers local (plutôt que d'utiliser la console Firebase), vous devez exécuter la commande suivante :

firebase deploy --only firestore:rules

Dans cet atelier de programmation, vous avez appris à effectuer des lectures et des écritures de base et avancées avec Cloud Firestore, ainsi qu'à sécuriser l'accès aux données avec des règles de sécurité. Vous pouvez trouver la solution complète dans le référentiel quickstarts-js .

Pour en savoir plus sur Cloud Firestore, consultez les ressources suivantes :