Cloud Firestore Web Codelab

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

1. Panoramica

Obiettivi

In questo codelab, creerai un'app Web di consigli sui ristoranti basata su Cloud Firestore .

img5.png

Cosa imparerai

  • Leggi e scrivi dati su Cloud Firestore da un'app web
  • Ascolta le modifiche ai dati di Cloud Firestore in tempo reale
  • Usa l'autenticazione Firebase e le regole di sicurezza per proteggere i dati di Cloud Firestore
  • Scrivi complesse query Cloud Firestore

Di cosa avrai bisogno

Prima di avviare questo codelab, assicurati di aver installato:

2. Crea e imposta un progetto Firebase

Crea un progetto Firebase

  1. Nella console Firebase , fai clic su Aggiungi progetto , quindi denomina il progetto Firebase FriendlyEats .

Ricorda l'ID progetto per il tuo progetto Firebase.

  1. Fare clic su Crea progetto .

L'applicazione che creeremo 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 specifico codelab, 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 disporre di una qualche forma di autenticazione nella nostra app. Utilizzeremo l' accesso anonimo , il che significa che l'utente accederà silenziosamente senza che venga richiesto.

Dovrai abilitare l' accesso anonimo.

  1. Nella console Firebase, individua la sezione Costruisci nella barra di navigazione a sinistra.
  2. Fai clic su Autenticazione , quindi fai clic sulla scheda Metodo di accesso (o fai clic qui per andare direttamente lì).
  3. Abilita il provider di accesso anonimo , quindi fai clic su Salva .

img7.png

Ciò consentirà all'applicazione di accedere in modo invisibile 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 Database Firestore . Fai clic su Crea database nel riquadro Cloud Firestore.

L'accesso ai dati in Cloud Firestore è controllato dalle regole di sicurezza. Parleremo di più 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 precedenti limitano l'accesso ai dati agli utenti che hanno effettuato l'accesso, impedendo 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

Clona il repository GitHub dalla riga di comando:

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

Il codice di esempio avrebbe dovuto essere 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 di avviamento

Usando 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. Installare 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.

  1. Installa la CLI eseguendo il seguente comando npm:
npm -g install firebase-tools
  1. Verificare che la CLI sia stata installata correttamente eseguendo il comando seguente:
firebase --version

Assicurati che la versione dell'interfaccia a riga di comando di Firebase sia v7.4.0 o successiva.

  1. Autorizza l'interfaccia a riga di comando di Firebase eseguendo il comando seguente:
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.

  1. Assicurati che la tua riga di comando stia accedendo alla directory locale della tua app.
  2. Associa la tua app al tuo progetto Firebase eseguendo il comando seguente:
firebase use --add
  1. 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, utilizziamo semplicemente l'alias di default .

  1. Segui le restanti istruzioni nella riga di comando.

5. Eseguire il server locale

Siamo pronti per iniziare effettivamente a lavorare sulla nostra app! Eseguiamo la nostra app localmente!

  1. Esegui il seguente comando CLI Firebase:
firebase emulators:start --only hosting
  1. La tua 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 in locale. L'app Web dovrebbe ora essere disponibile da http://localhost:5000 .

  1. 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 automaticamente come utente anonimo.

img2.png

6. Scrivi i dati su Cloud Firestore

In questa sezione, scriveremo alcuni dati su Cloud Firestore in modo da poter popolare l'interfaccia utente dell'app. Questo può essere fatto manualmente tramite la console Firebase , ma lo faremo nell'app stessa per dimostrare una scrittura di base su Cloud Firestore.

Modello di dati

I dati di Firestore sono suddivisi in raccolte, documenti, campi e sottoraccolte. Conserveremo ogni ristorante come documento in una raccolta di livello superiore denominata restaurants .

img3.png

Successivamente, memorizzeremo ogni recensione in una sottoraccolta denominata ratings sotto ogni ristorante.

img4.png

Aggiungi ristoranti a Firestore

L'oggetto modello principale nella nostra app è un ristorante. Scriviamo del codice che aggiunga un documento ristorante alla collezione restaurants .

  1. Dai file scaricati, apri scripts/FriendlyEats.Data.js .
  2. Trova la funzione FriendlyEats.prototype.addRestaurant .
  3. Sostituisci l'intera funzione con il codice seguente.

FriendlyEats.Data.js

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

Il codice sopra aggiunge un nuovo documento alla collezione dei restaurants . I dati del documento provengono da un semplice oggetto JavaScript. Lo facciamo ottenendo prima un riferimento a una raccolta di restaurants di Cloud Firestore, quindi add "i dati".

Aggiungiamo ristoranti!

  1. Torna alla tua app FriendlyEats nel tuo browser e aggiornala.
  2. 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 Firebase, ora dovresti vedere nuovi documenti nella raccolta dei restaurants !

img6.png

Congratulazioni, hai appena scritto dati su Cloud Firestore da un'app web!

Nella prossima sezione 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.

Innanzitutto, costruiamo la query che servirà l'elenco di ristoranti predefinito e non filtrato.

  1. Torna al file scripts/FriendlyEats.Data.js .
  2. Trova la funzione FriendlyEats.prototype.getAllRestaurants .
  3. Sostituisci l'intera funzione con il codice seguente.

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 livello superiore denominata restaurants , ordinati in base alla valutazione media (attualmente tutti 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.

  1. Torna al file scripts/FriendlyEats.Data.js .
  2. Trova la funzione FriendlyEats.prototype.getDocumentsInQuery .
  3. Sostituisci l'intera funzione con il codice seguente.

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 precedente, query.onSnapshot attiverà la sua richiamata ogni volta che viene apportata 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 di restaurants da Cloud Firestore. Passa quindi tutti i singoli documenti alla funzione renderer.display .
  • Quando un documento viene eliminato, change.type equivale a removed . 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 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 accedere alla console Firebase ed eliminare manualmente un ristorante o cambiarne il nome: vedrai immediatamente le modifiche sul tuo sito!

img5.png

8. Ottieni () 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.

Vorremo implementare un metodo che viene attivato quando un utente fa clic su un ristorante specifico nella tua app.

  1. Torna al tuo file scripts/FriendlyEats.Data.js .
  2. Trova la funzione FriendlyEats.prototype.getRestaurant .
  3. Sostituisci l'intera funzione con il codice seguente.

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:

img1.png

Per ora, non puoi aggiungere valutazioni poiché dobbiamo ancora implementare l'aggiunta di valutazioni in un secondo momento nel codelab.

9. Ordina e filtra 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 filtraggio.

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 la cui category è Dim Sum .

Nella nostra app, l'utente può concatenare più filtri per creare query specifiche, come "Pizza a San Francisco" o "Frutti di mare a Los Angeles ordinati per popolarità".

Creeremo un metodo che creerà una query che filtrerà i nostri ristoranti in base a più criteri selezionati dai nostri utenti.

  1. Torna al tuo file scripts/FriendlyEats.Data.js .
  2. Trova la funzione FriendlyEats.prototype.getFilteredRestaurants .
  3. Sostituisci l'intera funzione con il codice seguente.

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 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/.../database/firestore/indexes?create_index=...

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 compilati. Nella sezione successiva, scriveremo e distribuiremo gli indici necessari per questa applicazione.

10. Distribuire gli indici

Se non vogliamo esplorare tutti i percorsi nella nostra app e seguire ciascuno dei link di creazione dell'indice, possiamo facilmente distribuire più indici contemporaneamente utilizzando l'interfaccia a riga di comando di Firebase.

  1. 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" }
     ]
   },

   ...

 ]
}
  1. Distribuisci questi indici con il comando seguente:
firebase deploy --only firestore:indexes

Dopo alcuni 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 essi ha commesso un errore, probabilmente chiederemmo all'utente di riprovare o la nostra app ritenterebbe la scrittura automaticamente.

La nostra app avrà molti utenti che vogliono 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 la valutazione 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'altra.

Fortunatamente, Cloud Firestore offre funzionalità di transazione che ci consentono di eseguire più letture e scritture in un'unica operazione atomica, assicurando che i nostri dati rimangano coerenti.

  1. Torna al tuo file scripts/FriendlyEats.Data.js .
  2. Trova la funzione FriendlyEats.prototype.addRating .
  3. Sostituisci l'intera funzione con il codice seguente.

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 delle 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ù dettagliate per prevenire l'accesso o la modifica indesiderata dei dati.

  1. Nella sezione Build della console Firebase, fai clic su Database Firestore .
  2. Fai clic sulla scheda Regole nella sezione Cloud Firestore (o fai clic qui per andare direttamente lì).
  3. Sostituisci le impostazioni predefinite con le seguenti regole, quindi fai clic su Pubblica .

regole.firestore

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 a un documento del ristorante possono modificare solo le valutazioni, non il nome o altri dati immutabili.
  • Le classificazioni 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 (piuttosto che utilizzare la console Firebase), devi eseguire il comando seguente:

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: