1. Vue d'ensemble
Objectifs
Dans cet atelier de programmation, vous allez créer une application Web de recommandation de restaurants optimisée par Cloud Firestore .
Ce que vous apprendrez
- Lire et écrire des données sur Cloud Firestore à partir d'une application Web
- Écoutez les changements dans les données Cloud Firestore en temps réel
- Utilisez 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 démarrer cet atelier de programmation, assurez-vous d'avoir installé :
2. Créez et configurez un projet Firebase
Créer un projet Firebase
- 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.
- Cliquez sur Créer un projet .
L'application que nous allons construire 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 guiderons à travers la configuration et l'activation des services à l'aide de la console Firebase.
Activer l'authentification anonyme
Bien que l'authentification ne soit pas au centre de cet atelier de programmation, il est important de disposer d'une certaine forme d'authentification dans notre application. Nous utiliserons la connexion anonyme , ce qui signifie que l'utilisateur sera connecté silencieusement sans y être invité.
Vous devrez activer la connexion anonyme.
- Dans la console Firebase, localisez la section Build dans le menu de gauche.
- Cliquez sur Authentification , puis cliquez sur l'onglet Méthode de connexion (ou cliquez ici pour y accéder directement).
- Activez le fournisseur de connexion anonyme , puis cliquez sur Enregistrer .
Cela permettra à l'application de 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 devrez 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 davantage 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 l'atelier de programmation.
3. Obtenez l'exemple de code
Clonez le dépôt GitHub à partir de la ligne de commande :
git clone https://github.com/firebase/friendlyeats-web
L'exemple de code aurait dû être cloné dans le répertoire 📁 friendlyeats-web
. Désormais, assurez-vous d'exécuter toutes vos commandes depuis ce répertoire :
cd friendlyeats-web/vanilla-js
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épart de l'atelier 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.
4. Installez l'interface de ligne de commande Firebase
L'interface de ligne de commande (CLI) Firebase vous permet de servir votre application Web localement et de déployer votre application Web sur Firebase Hosting.
- Installez la CLI en exécutant la commande npm suivante :
npm -g install firebase-tools
- 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 version 7.4.0 ou ultérieure.
- 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 Firebase Hosting à partir du répertoire et des fichiers locaux de votre application. Mais pour ce faire, nous devons associer votre application à votre projet Firebase.
- Assurez-vous que votre ligne de commande accède au répertoire local de votre application.
- Associez votre application à votre projet Firebase en exécutant la commande suivante :
firebase use --add
- Lorsque vous y êtes invité, sélectionnez votre ID de projet , puis attribuez un alias à votre projet Firebase.
Un alias est utile si vous disposez de plusieurs environnements (production, staging, etc.). Cependant, pour cet atelier de programmation, utilisons simplement l'alias de default
.
- Suivez les instructions restantes dans votre ligne de commande.
5. Exécutez le serveur local
Nous sommes prêts à commencer à travailler sur notre application ! Exécutons notre application localement !
- Exécutez la commande Firebase CLI suivante :
firebase emulators:start --only hosting
- Votre ligne de commande devrait afficher la réponse suivante :
hosting: Local server: http://localhost:5000
Nous utilisons l'émulateur Firebase Hosting pour diffuser notre application localement. L'application Web devrait maintenant être disponible sur http://localhost:5000 .
- 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.
6. Écrivez des données sur Cloud Firestore
Dans cette section, nous allons écrire certaines 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 dans Cloud Firestore.
Modèle de données
Les données Firestore sont divisées en collections, documents, champs et sous-collections. Nous stockerons chaque restaurant sous forme de document dans une collection de niveau supérieur appelée restaurants
.
Plus tard, nous stockerons chaque avis dans une sous-collection appelée ratings
sous chaque restaurant.
Ajouter des restaurants à Firestore
L'objet modèle principal de notre application est un restaurant. Écrivons du code qui ajoute un document de restaurant à la collection restaurants
.
- À partir de vos fichiers téléchargés, ouvrez
scripts/FriendlyEats.Data.js
. - Recherchez la fonction
FriendlyEats.prototype.addRestaurant
. - 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 restaurants
. Les données du document proviennent d'un simple objet JavaScript. Nous faisons cela en obtenant d'abord une référence à une collection restaurants
Cloud Firestore, puis add
les données.
Ajoutons les restaurants !
- Revenez à votre application FriendlyEats dans votre navigateur et actualisez-la.
- Cliquez sur Ajouter des données fictives .
L'application générera automatiquement un ensemble aléatoire d'objets 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 de l'atelier de programmation).
Si vous accédez à l' onglet Cloud Firestore dans la console Firebase, vous devriez maintenant voir de nouveaux documents dans la collection restaurants
!
Félicitations, vous venez d'écrire des données dans Cloud Firestore à partir d'une application Web !
Dans la section suivante, vous apprendrez comment récupérer des données de Cloud Firestore et les afficher dans votre application.
7. Afficher les données de Cloud Firestore
Dans cette section, vous apprendrez comment récupérer des données de 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 auditeur sera informé de toutes les données existantes correspondant à 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.
- Revenez au fichier
scripts/FriendlyEats.Data.js
. - Recherchez la fonction
FriendlyEats.prototype.getAllRestaurants
. - 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 niveau supérieur 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 ferons cela en ajoutant un écouteur d'instantané.
- Revenez au fichier
scripts/FriendlyEats.Data.js
. - Recherchez la fonction
FriendlyEats.prototype.getDocumentsInQuery
. - 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 que le résultat de la requête sera modifié.
- 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
restaurants
de Cloud Firestore. Il transmet ensuite tous les documents individuels à la fonctionrenderer.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 auditeur continuera à se mettre à jour automatiquement. Essayez d'accéder à la console Firebase et de supprimer manuellement un restaurant ou de modifier son nom : vous verrez les modifications apparaître immédiatement sur votre site !
8. Obtenir des données()
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 souhaitons. Parfois, il est plus judicieux de récupérer les données 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.
- Revenez à votre fichier
scripts/FriendlyEats.Data.js
. - Recherchez la fonction
FriendlyEats.prototype.getRestaurant
. - 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 mis en œuvre 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 de détails du restaurant :
Pour l'instant, vous ne pouvez pas ajouter de notes, car nous devons encore implémenter l'ajout de notes ultérieurement dans l'atelier de programmation.
9. Trier et filtrer les données
Actuellement, notre application affiche une liste de restaurants, mais l'utilisateur n'a aucun moyen 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 en sorte que notre requête télécharge uniquement les membres de la collection dont les champs répondent aux restrictions que nous avons définies. Dans ce cas, seuls les restaurants dont category
est Dim Sum
seront téléchargés.
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 classé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.
- Revenez à votre fichier
scripts/FriendlyEats.Data.js
. - Recherchez la fonction
FriendlyEats.prototype.getFilteredRestaurants
. - 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 la saisie 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/project-id/database/firestore/indexes?create_composite=...
Ces erreurs sont dues au fait que Cloud Firestore nécessite des index pour la plupart des requêtes composées. L'exigence d'index sur les requêtes permet à Cloud Firestore d'évoluer rapidement.
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 écrirons et déploierons les index nécessaires à cette application.
10. Déployer des index
Si nous ne souhaitons 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.
- 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 à toutes les combinaisons possibles de filtres.
firestore.indexes.json
{ "indexes": [ { "collectionGroup": "restaurants", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "city", "order": "ASCENDING" }, { "fieldPath": "avgRating", "order": "DESCENDING" } ] }, ... ] }
- Déployez ces index avec la commande suivante :
firebase deploy --only firestore:indexes
Après quelques minutes, vos index seront actifs et les messages d'erreur disparaîtront.
11. Écrire des données dans une transaction
Dans cette section, nous ajouterons la possibilité pour les utilisateurs de soumettre des avis sur les restaurants. Jusqu’à présent, toutes nos écritures ont été atomiques et relativement simples. Si l'un d'entre eux comportait une erreur, nous inviterions probablement simplement l'utilisateur à le réessayer ou notre application réessayerait automatiquement l'écriture.
De nombreux utilisateurs de notre application souhaiteront ajouter une note à un restaurant. Nous devrons donc coordonner plusieurs lectures et écritures. L'avis lui-même doit d'abord être soumis, puis le count
de notes et average rating
du restaurant doivent être mis à jour. Si l’un d’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.
- Revenez à votre fichier
scripts/FriendlyEats.Data.js
. - Recherchez la fonction
FriendlyEats.prototype.addRating
. - 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 ratings
.
12. Sécurisez vos données
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 tout accès ou modification indésirable aux données.
- Dans la section Build de la console Firebase, cliquez sur Firestore Database .
- Cliquez sur l'onglet Règles dans la section Cloud Firestore (ou cliquez ici pour y accéder directement).
- 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 effectuent uniquement des modifications sécurisées. Par exemple:
- 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é.
Alternativement à la console Firebase, vous pouvez utiliser la CLI Firebase pour déployer des règles sur 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
13. Conclusion
Dans cet atelier de programmation, vous avez appris à effectuer des lectures et é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 :
14. [Facultatif] Appliquer avec App Check
Firebase App Check offre une protection en aidant à valider et à empêcher le trafic indésirable vers votre application. Dans cette étape, vous sécuriserez l'accès à vos services en ajoutant App Check avec reCAPTCHA Enterprise .
Tout d’abord, vous devrez activer App Check et reCaptcha.
Activation de reCaptcha Entreprise
- Dans la console Cloud, recherchez et sélectionnez reCaptcha Enterprise sous Sécurité.
- Activez le service lorsque vous y êtes invité, puis cliquez sur Créer une clé .
- Saisissez un nom d'affichage lorsque vous y êtes invité et sélectionnez Site Web comme type de plate-forme.
- Ajoutez vos URL déployées à la liste des domaines et assurez-vous que l'option "Utiliser la vérification par case à cocher" n'est pas sélectionnée .
- Cliquez sur Créer une clé et stockez la clé générée quelque part pour la conserver. Vous en aurez besoin plus tard dans cette étape.
Activation de la vérification des applications
- Dans la console Firebase, localisez la section Build dans le panneau de gauche.
- Cliquez sur App Check , puis cliquez sur le bouton Get Started (ou redirigez directement vers la console ).
- Cliquez sur S'inscrire et saisissez votre clé reCaptcha Enterprise lorsque vous y êtes invité, puis cliquez sur Enregistrer .
- Dans la vue API, sélectionnez Stockage et cliquez sur Appliquer . Faites de même pour Cloud Firestore .
App Check devrait maintenant être appliqué ! Actualisez votre application et essayez de créer/afficher un restaurant. Vous devriez obtenir le message d'erreur :
Uncaught Error in snapshot listener: FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
Cela signifie qu'App Check bloque les demandes non validées par défaut. Ajoutons maintenant la validation à votre application.
Accédez au fichier FriendlyEats.View.js , mettez à jour la fonction initAppCheck
et ajoutez votre clé reCaptcha pour initialiser App Check.
FriendlyEats.prototype.initAppCheck = function() {
var appCheck = firebase.appCheck();
appCheck.activate(
new firebase.appCheck.ReCaptchaEnterpriseProvider(
/* reCAPTCHA Enterprise site key */
),
true // Set to true to allow auto-refresh.
);
};
L'instance appCheck
est initialisée avec un ReCaptchaEnterpriseProvider
avec votre clé, et isTokenAutoRefreshEnabled
permet aux jetons de s'actualiser automatiquement dans votre application.
Pour activer les tests locaux, recherchez la section dans laquelle l'application est initialisée dans le fichier FriendlyEats.js et ajoutez la ligne suivante à la fonction FriendlyEats.prototype.initAppCheck
:
if(isLocalhost) {
self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}
Cela enregistrera un jeton de débogage dans la console de votre application Web locale similaire à :
App Check debug token: 8DBDF614-649D-4D22-B0A3-6D489412838B. You will need to add it to your app's App Check settings in the Firebase console for it to work.
Maintenant, accédez à la vue Applications d’App Check dans la console Firebase.
Cliquez sur le menu de débordement et sélectionnez Gérer les jetons de débogage .
Ensuite, cliquez sur Ajouter un jeton de débogage et collez le jeton de débogage depuis votre console comme vous y êtes invité.
Toutes nos félicitations! App Check devrait maintenant fonctionner dans votre application.