1. Panoramica
Obiettivi
In questo codelab, creerai un'app web per consigli sui ristoranti basata su Cloud Firestore .
Cosa imparerai
- Leggere e scrivere dati su Cloud Firestore da un'app Web
- Ascolta i cambiamenti nei dati di Cloud Firestore in tempo reale
- Usa l'autenticazione Firebase e le regole di sicurezza per proteggere i dati di Cloud Firestore
- Scrivi query Cloud Firestore complesse
Di cosa avrai bisogno
Prima di iniziare questo codelab, assicurati di aver installato:
2. Crea e configura un progetto Firebase
Crea un progetto Firebase
- Nella console Firebase , fai clic su Aggiungi progetto , quindi assegna al progetto Firebase il nome FriendlyEats .
Ricorda l'ID progetto per il tuo progetto Firebase.
- Fai clic su Crea progetto .
L'applicazione che costruiremo utilizza alcuni servizi Firebase disponibili sul web:
- Autenticazione Firebase per identificare facilmente i tuoi utenti
- Cloud Firestore per salvare i dati strutturati sul Cloud e ricevere notifiche istantanee quando i dati vengono aggiornati
- Firebase Hosting per ospitare e servire le tue risorse statiche
Per questo codelab specifico, abbiamo già configurato Firebase Hosting. Tuttavia, per Firebase Auth e Cloud Firestore, ti guideremo attraverso la configurazione e l'abilitazione dei servizi utilizzando la console Firebase.
Abilita autenticazione anonima
Sebbene l'autenticazione non sia l'obiettivo di questo codelab, è importante avere una qualche forma di autenticazione nella nostra app. Useremo l'accesso anonimo , il che significa che l'utente accederà silenziosamente senza che venga richiesto.
Dovrai abilitare l'accesso anonimo.
- Nella console Firebase, individua la sezione Build nel menu di navigazione sinistro.
- Fai clic su Autenticazione , quindi fai clic sulla scheda Metodo di accesso (o fai clic qui per andare direttamente lì).
- Abilita il provider di accesso anonimo , quindi fai clic su Salva .
Ciò consentirà all'applicazione di accedere automaticamente agli utenti quando accedono all'app Web. Sentiti libero di leggere la documentazione sull'autenticazione anonima per saperne di più.
Abilita Cloud Firestore
L'app utilizza Cloud Firestore per salvare e ricevere informazioni e valutazioni sui ristoranti.
Dovrai abilitare Cloud Firestore. Nella sezione Build della console Firebase, fai clic su Firestore Database . Fai clic su Crea database nel riquadro Cloud Firestore.
L'accesso ai dati in Cloud Firestore è controllato dalle regole di sicurezza. Parleremo più approfonditamente delle regole più avanti in questo codelab, ma prima dobbiamo impostare alcune regole di base sui nostri dati per iniziare. Nella scheda Regole della console Firebase aggiungi le seguenti regole e fai clic su Pubblica .
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; } } }
Le regole di cui sopra limitano l'accesso ai dati agli utenti che hanno effettuato l'accesso, il che impedisce agli utenti non autenticati di leggere o scrivere. Questo è meglio che consentire l'accesso pubblico ma è ancora lontano dall'essere sicuro, miglioreremo queste regole più avanti nel codelab.
3. Ottieni il codice di esempio
Clonare il repository GitHub dalla riga di comando:
git clone https://github.com/firebase/friendlyeats-web
Il codice di esempio dovrebbe essere stato clonato nella directory 📁 friendlyeats-web
. D'ora in poi, assicurati di eseguire tutti i tuoi comandi da questa directory:
cd friendlyeats-web
Importa l'app iniziale
Utilizzando il tuo IDE (WebStorm, Atom, Sublime, Visual Studio Code...) apri o importa la directory 📁 friendlyeats-web
. Questa directory contiene il codice iniziale per il codelab che consiste in un'app di consigli sui ristoranti non ancora funzionante. Lo renderemo funzionale in tutto questo codelab, quindi presto dovrai modificare il codice in quella directory.
4. Installa l'interfaccia della riga di comando di Firebase
L'interfaccia a riga di comando (CLI) di Firebase ti consente di servire la tua app Web localmente e di distribuire la tua app Web su Firebase Hosting.
- Installa la CLI eseguendo il seguente comando npm:
npm -g install firebase-tools
- Verificare che la CLI sia stata installata correttamente eseguendo il seguente comando:
firebase --version
Assicurati che la versione dell'interfaccia a riga di comando di Firebase sia v7.4.0 o successiva.
- Autorizza l'interfaccia a riga di comando di Firebase eseguendo il seguente comando:
firebase login
Abbiamo impostato il modello di app Web per estrarre la configurazione dell'app per Firebase Hosting dalla directory e dai file locali dell'app. Ma per fare ciò, dobbiamo associare la tua app al tuo progetto Firebase.
- Assicurati che la riga di comando acceda alla directory locale dell'app.
- Associa la tua app al tuo progetto Firebase eseguendo il seguente comando:
firebase use --add
- Quando richiesto, seleziona il tuo ID progetto , quindi assegna un alias al tuo progetto Firebase.
Un alias è utile se hai più ambienti (produzione, staging, ecc.). Tuttavia, per questo codelab, usiamo semplicemente l'alias di default
.
- Segui le istruzioni rimanenti nella riga di comando.
5. Eseguire il server locale
Siamo pronti per iniziare effettivamente a lavorare sulla nostra app! Eseguiamo la nostra app in locale!
- Esegui il seguente comando CLI di Firebase:
firebase emulators:start --only hosting
- La riga di comando dovrebbe visualizzare la seguente risposta:
hosting: Local server: http://localhost:5000
Stiamo utilizzando l'emulatore Firebase Hosting per servire la nostra app localmente. L'app Web dovrebbe ora essere disponibile da http://localhost:5000 .
- Apri la tua app su http://localhost:5000 .
Dovresti vedere la tua copia di FriendlyEats che è stata collegata al tuo progetto Firebase.
L'app si è connessa automaticamente al tuo progetto Firebase e ti ha registrato silenziosamente come utente anonimo.
6. Scrivi i dati su Cloud Firestore
In questa sezione, scriveremo alcuni dati in Cloud Firestore in modo da poter popolare l'interfaccia utente dell'app. Questa operazione può essere eseguita manualmente tramite la console Firebase , ma lo faremo nell'app stessa per dimostrare una scrittura di base di Cloud Firestore.
Modello di dati
I dati di Firestore sono suddivisi in raccolte, documenti, campi e sottoraccolte. Memorizzeremo ogni ristorante come documento in una raccolta di primo livello chiamata restaurants
.
Successivamente, memorizzeremo ogni recensione in una sottoraccolta chiamata ratings
sotto ogni ristorante.
Aggiungi ristoranti a Firestore
L'oggetto modello principale nella nostra app è un ristorante. Scriviamo un codice che aggiunga un documento restaurant alla raccolta restaurants
.
- Dai file scaricati, apri
scripts/FriendlyEats.Data.js
. - Trova la funzione
FriendlyEats.prototype.addRestaurant
. - Sostituire l'intera funzione con il seguente codice.
FriendlyEats.Data.js
FriendlyEats.prototype.addRestaurant = function(data) { var collection = firebase.firestore().collection('restaurants'); return collection.add(data); };
Il codice precedente aggiunge un nuovo documento alla raccolta restaurants
. I dati del documento provengono da un semplice oggetto JavaScript. Per farlo, otteniamo prima un riferimento a una raccolta di restaurants
di Cloud Firestore, quindi add
i dati.
Aggiungiamo ristoranti!
- Torna alla tua app FriendlyEats nel tuo browser e aggiornala.
- Fare clic su Aggiungi dati fittizi .
L'app genererà automaticamente un insieme casuale di oggetti ristoranti, quindi chiamerà la funzione addRestaurant
. Tuttavia, non vedrai ancora i dati nella tua app Web effettiva perché dobbiamo ancora implementare il recupero dei dati (la sezione successiva del codelab).
Se accedi alla scheda Cloud Firestore nella console di Firebase, tuttavia, ora dovresti vedere nuovi documenti nella raccolta restaurants
!
Congratulazioni, hai appena scritto i dati in Cloud Firestore da un'app Web!
Nella sezione successiva imparerai come recuperare i dati da Cloud Firestore e visualizzarli nella tua app.
7. Visualizza i dati da Cloud Firestore
In questa sezione imparerai come recuperare i dati da Cloud Firestore e visualizzarli nella tua app. I due passaggi chiave sono la creazione di una query e l'aggiunta di un listener di snapshot. Questo listener riceverà una notifica di tutti i dati esistenti che corrispondono alla query e riceverà aggiornamenti in tempo reale.
Per prima cosa, costruiamo la query che servirà l'elenco di ristoranti predefinito e non filtrato.
- Torna al file
scripts/FriendlyEats.Data.js
. - Trova la funzione
FriendlyEats.prototype.getAllRestaurants
. - Sostituire l'intera funzione con il seguente codice.
FriendlyEats.Data.js
FriendlyEats.prototype.getAllRestaurants = function(renderer) { var query = firebase.firestore() .collection('restaurants') .orderBy('avgRating', 'desc') .limit(50); this.getDocumentsInQuery(query, renderer); };
Nel codice sopra, costruiamo una query che recupererà fino a 50 ristoranti dalla raccolta di primo livello denominata restaurants
, ordinati in base alla valutazione media (attualmente tutti pari a zero). Dopo aver dichiarato questa query, la passiamo al metodo getDocumentsInQuery()
che è responsabile del caricamento e del rendering dei dati.
Lo faremo aggiungendo un listener di istantanee.
- Torna al file
scripts/FriendlyEats.Data.js
. - Trova la funzione
FriendlyEats.prototype.getDocumentsInQuery
. - Sostituire l'intera funzione con il seguente codice.
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); } }); }); };
Nel codice sopra, query.onSnapshot
attiverà la sua richiamata ogni volta che c'è una modifica al risultato della query.
- La prima volta, la richiamata viene attivata con l'intero set di risultati della query, ovvero l'intera raccolta
restaurants
da Cloud Firestore. Quindi passa tutti i singoli documenti alla funzionerenderer.display
. - Quando un documento viene eliminato,
change.type
equivale aremoved
. Quindi, in questo caso, chiameremo una funzione che rimuove il ristorante dall'interfaccia utente.
Ora che abbiamo implementato entrambi i metodi, aggiorna l'app e verifica che i ristoranti che abbiamo visto in precedenza nella console Firebase siano ora visibili nell'app. Se hai completato correttamente questa sezione, la tua app ora sta leggendo e scrivendo dati con Cloud Firestore!
Man mano che il tuo elenco di ristoranti cambia, questo listener continuerà ad aggiornarsi automaticamente. Prova ad andare alla console di Firebase e a eliminare manualmente un ristorante o a cambiarne il nome: vedrai immediatamente le modifiche sul tuo sito!
8. Get() dati
Finora abbiamo mostrato come utilizzare onSnapshot
per recuperare gli aggiornamenti in tempo reale; tuttavia, non è sempre quello che vogliamo. A volte ha più senso recuperare i dati solo una volta.
Vorremmo implementare un metodo che viene attivato quando un utente fa clic su un ristorante specifico nella tua app.
- Torna al tuo file
scripts/FriendlyEats.Data.js
. - Trova la funzione
FriendlyEats.prototype.getRestaurant
. - Sostituire l'intera funzione con il seguente codice.
FriendlyEats.Data.js
FriendlyEats.prototype.getRestaurant = function(id) { return firebase.firestore().collection('restaurants').doc(id).get(); };
Dopo aver implementato questo metodo, sarai in grado di visualizzare le pagine per ogni ristorante. Basta fare clic su un ristorante nell'elenco e dovresti vedere la pagina dei dettagli del ristorante:
Per ora, non puoi aggiungere valutazioni perché dobbiamo ancora implementare l'aggiunta di valutazioni in un secondo momento nel codelab.
9. Ordinare e filtrare i dati
Attualmente, la nostra app mostra un elenco di ristoranti, ma non c'è modo per l'utente di filtrare in base alle proprie esigenze. In questa sezione, utilizzerai le query avanzate di Cloud Firestore per abilitare il filtro.
Ecco un esempio di una semplice query per recuperare tutti i ristoranti Dim Sum
:
var filteredQuery = query.where('category', '==', 'Dim Sum')
Come suggerisce il nome, il metodo where()
farà in modo che la nostra query scarichi solo i membri della raccolta i cui campi soddisfano le restrizioni che abbiamo impostato. In questo caso, scaricherà solo i ristoranti in cui category
è Dim Sum
.
Nella nostra app, l'utente può concatenare più filtri per creare query specifiche, come "Pizza a San Francisco" o "Pesce a Los Angeles ordinati per popolarità".
Creeremo un metodo che crea una query che filtrerà i nostri ristoranti in base a più criteri selezionati dai nostri utenti.
- Torna al tuo file
scripts/FriendlyEats.Data.js
. - Trova la funzione
FriendlyEats.prototype.getFilteredRestaurants
. - Sostituire l'intera funzione con il seguente codice.
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); };
Il codice precedente aggiunge più filtri where
e una singola clausola orderBy
per creare una query composta basata sull'input dell'utente. La nostra query ora restituirà solo i ristoranti che soddisfano i requisiti dell'utente.
Aggiorna la tua app FriendlyEats nel tuo browser, quindi verifica di poter filtrare per prezzo, città e categoria. Durante il test, vedrai errori nella console JavaScript del tuo browser che assomigliano a questo:
The query requires an index. You can create it here: https://console.firebase.google.com/project/project-id/database/firestore/indexes?create_composite=...
Questi errori sono dovuti al fatto che Cloud Firestore richiede indici per la maggior parte delle query composte. La richiesta di indici sulle query mantiene Cloud Firestore veloce su larga scala.
L'apertura del collegamento dal messaggio di errore aprirà automaticamente l'interfaccia utente di creazione dell'indice nella console Firebase con i parametri corretti inseriti. Nella sezione successiva, scriveremo e distribuiremo gli indici necessari per questa applicazione.
10. Distribuire gli indici
Se non vogliamo esplorare ogni percorso nella nostra app e seguire ciascuno dei collegamenti per la creazione dell'indice, possiamo distribuire facilmente molti indici contemporaneamente utilizzando l'interfaccia a riga di comando di Firebase.
- Nella directory locale scaricata della tua app troverai un file
firestore.indexes.json
.
Questo file descrive tutti gli indici necessari per tutte le possibili combinazioni di filtri.
firestore.indexes.json
{ "indexes": [ { "collectionGroup": "restaurants", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "city", "order": "ASCENDING" }, { "fieldPath": "avgRating", "order": "DESCENDING" } ] }, ... ] }
- Distribuisci questi indici con il seguente comando:
firebase deploy --only firestore:indexes
Dopo pochi minuti, i tuoi indici saranno attivi e i messaggi di errore scompariranno.
11. Scrivi i dati in una transazione
In questa sezione, aggiungeremo la possibilità per gli utenti di inviare recensioni ai ristoranti. Finora, tutte le nostre scritture sono state atomiche e relativamente semplici. Se qualcuno di loro ha commesso un errore, probabilmente chiederemo semplicemente all'utente di ritentarlo o la nostra app ritenterà automaticamente la scrittura.
La nostra app avrà molti utenti che desiderano aggiungere una valutazione per un ristorante, quindi dovremo coordinare più letture e scritture. Per prima cosa è necessario inviare la recensione stessa, quindi è necessario aggiornare il count
delle valutazioni del ristorante e average rating
. Se uno di questi fallisce ma non l'altro, rimaniamo in uno stato incoerente in cui i dati in una parte del nostro database non corrispondono ai dati in un altro.
Fortunatamente, Cloud Firestore fornisce funzionalità di transazione che ci consentono di eseguire più letture e scritture in un'unica operazione atomica, assicurando che i nostri dati rimangano coerenti.
- Torna al tuo file
scripts/FriendlyEats.Data.js
. - Trova la funzione
FriendlyEats.prototype.addRating
. - Sostituire l'intera funzione con il seguente codice.
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); }); }); };
Nel blocco sopra, attiviamo una transazione per aggiornare i valori numerici di avgRating
e numRatings
nel documento del ristorante. Allo stesso tempo, aggiungiamo la nuova rating
alla sottoraccolta ratings
.
12. Proteggi i tuoi dati
All'inizio di questo codelab, impostiamo le regole di sicurezza della nostra app per aprire completamente il database a qualsiasi lettura o scrittura. In un'applicazione reale, vorremmo impostare regole molto più granulari per impedire l'accesso o la modifica indesiderata dei dati.
- Nella sezione Build della console Firebase, fai clic su Firestore Database .
- Fai clic sulla scheda Regole nella sezione Cloud Firestore (o fai clic qui per andare direttamente lì).
- Sostituisci i valori predefiniti con le seguenti regole, quindi fai clic su Pubblica .
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; } } } }
Queste regole limitano l'accesso per garantire che i client apportino solo modifiche sicure. Per esempio:
- Gli aggiornamenti al documento di un ristorante possono modificare solo le valutazioni, non il nome o altri dati immutabili.
- Le valutazioni possono essere create solo se l'ID utente corrisponde all'utente che ha eseguito l'accesso, il che impedisce lo spoofing.
In alternativa all'utilizzo della console Firebase, puoi utilizzare l'interfaccia a riga di comando di Firebase per distribuire le regole al tuo progetto Firebase. Il file firestore.rules nella tua directory di lavoro contiene già le regole di cui sopra. Per distribuire queste regole dal tuo filesystem locale (anziché utilizzare la console Firebase), devi eseguire il seguente comando:
firebase deploy --only firestore:rules
13. Conclusione
In questo codelab, hai imparato come eseguire letture e scritture di base e avanzate con Cloud Firestore, nonché come proteggere l'accesso ai dati con regole di sicurezza. Puoi trovare la soluzione completa nel repository quickstarts-js .
Per ulteriori informazioni su Cloud Firestore, visita le seguenti risorse: