Spotted a bug? Have a great idea? Help us make google.dev great!

Goals

In this codelab, you'll build a restaurant recommendation web app powered by Cloud Firestore.

What you'll learn

  • Read and write data to Cloud Firestore from a web app
  • Listen to changes in Cloud Firestore data in real time
  • Use a one-time get call to retrieve data from Cloud Firestore

What you'll need

Before starting this codelab, make sure that you've installed the following:

Create a Firebase project

  1. In the Firebase console, click Add project, and then name the Firebase project FriendlyEats. Remember the Project ID for your Firebase project.
  2. Click Create project.

The application that you're going to build uses a few Firebase services available on the web:

  • Firebase Authentication to easily identify your users
  • Cloud Firestore to save structured data on the Cloud and get instant notification when the data is updated
  • Firebase Hosting to host and serve your static assets

For this specific codelab, Firebase Hosting is already configured using the firebase.json config file. For Firebase Authentication and Cloud Firestore, the codelab instructions walk you through the configuration and enabling of the services, using the Firebase console.

Enable anonymous authentication

Although authentication isn't the focus of this codelab, it's important to have some form of authentication in your app. You'll use Anonymous login, meaning that the user will be silently signed in without being prompted.

Enable Anonymous login:

  1. In the Firebase console, click Develop in the left panel.
  2. Click Authentication, and then click the Sign-in method tab (or click here to go directly to the Sign-in method tab).
  3. Click Anonymous in the Sign-in providers list, set the Enable switch to the on position, and then click Save.

This allows the application to silently sign your users in when they access the web app. For more information, read the anonymous authentication documentation.

Enable Cloud Firestore

The app uses Cloud Firestore to save and receive restaurant information and ratings.

Enable Cloud Firestore:

  1. In the Firebase console's Develop section, click Database.
  2. Click Create database.

  1. Select the Start in test mode option, read the disclaimer about security rules, and then keeping the default location click Done.

By running the database in test mode, you can freely write to the database during development. You'll make your database more secure later on in this codelab.

Clone the GitHub repository from the command line:

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

The sample code should have been cloned into the 📁friendlyeats-web directory.

Change directory to the location of the sample code:

cd friendlyeats-web

For the rest of this codelab, you'll run commands from this location.

Import the starter app

Using your IDE (WebStorm, Atom, Sublime, Visual Studio Code...) open or import the 📁friendlyeats-web directory. This directory contains the starting code for the codelab, which consists of a not-yet-functional restaurant recommendation app. You'll make it functional throughout this codelab.

The Firebase command-line interface (CLI) allows you to serve your web app locally and deploy your web app to Firebase Hosting.

  1. Install the CLI by running the following command:
npm -g install firebase-tools
  1. Verify that the CLI has been installed correctly by running the following command:
firebase --version

Make sure that the version of the Firebase CLI is v6.2.0 or later.

  1. Authorize the Firebase CLI by running the following command:
firebase login

The web app template is set up to pull your app's configuration for Firebase Hosting from your app's local directory and files. For this to work, you need to associate your app with your Firebase project.

  1. On the command line, make sure you are in the root folder of the app (i.e. friendlyeats-web).
  1. Associate your app with your Firebase project by running the following command:
firebase use --add
  1. When prompted, select your Project ID, and then give your Firebase project an alias.

An alias is useful if you have multiple environments (such as production and staging). However, for this codelab, let's just use the alias of default.

  1. Follow the remaining instructions on the command line; if any.

You're ready to start work on your app! First, run your app locally.

  1. Run the following Firebase CLI command:
firebase serve --only hosting
  1. Your command line should display the following response:
hosting: Local server: http://localhost:5000

You're using the Firebase Hosting emulator to serve your app locally. The web app should now be available from http://localhost:5000.

  1. Open your app at http://localhost:5000.

You should see your copy of FriendlyEats, which has been connected to your Firebase project.

The app has automatically connected to your Firebase project and silently signed you in as an anonymous user.

In this section, you'll write some data to Cloud Firestore so that you can populate the app's UI. This can be done manually with the Firebase console, but you'll do it in the app itself to demonstrate a basic Cloud Firestore write operation.

Data model

Firestore data is split into collections, documents, fields, and subcollections. In FriendlyEats, you'll store each restaurant as a document in a top-level collection called restaurants.

Later, you'll store each review in a subcollection called ratings in each restaurant document.

Add restaurants to Firestore

The main model object in your app is a restaurant. Let's write some code that adds a restaurant document to the restaurants collection.

  1. From your downloaded files, open scripts/FriendlyEats.Data.js.
  2. Find the function FriendlyEats.prototype.addRestaurant.
  3. Replace the entire function with the following code:

FriendlyEats.Data.js

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

Let's add restaurants!

  1. Go back to your FriendlyEats app in your browser and refresh it.
  2. Click Add Mock Data.

The app automatically generates a random set of restaurant objects, and then calls the addRestaurant function. However, you won't yet see the data in your actual web app, because you still need to implement retrieving the data (the next section of the codelab).

Navigate to the Cloud Firestore tab in the Firebase console to see new documents in the restaurants collection!

Congratulations! You have just written data to Cloud Firestore from a web app.

In the next section, you'll learn how to retrieve data from Cloud Firestore and display it in your app.

In this section, you'll learn how to retrieve data from Cloud Firestore and display it in your app. The two key steps are creating a query and adding a snapshot listener. This listener will be notified of all existing data that matches the query and will receive updates in real time.

First, construct the query that serves the default, unfiltered list of restaurants:

  1. Go back to the file scripts/FriendlyEats.Data.js.
  2. Find the function FriendlyEats.prototype.getAllRestaurants.
  3. Replace the entire function with the following code:

FriendlyEats.Data.js

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

  this.getDocumentsInQuery(query, renderer);
};

The code above constructs a query that retrieves up to 50 restaurants from the top-level collection named restaurants, ordered by the average rating (currently all zero).

After the query is declared, it is passed to the getDocumentsInQuery() method, which is responsible for loading and rendering the data.

You do this by adding a snapshot listener:

  1. Go back to the file scripts/FriendlyEats.Data.js.
  2. Find the function FriendlyEats.prototype.getDocumentsInQuery.
  3. Replace the entire function with the following code:

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

In this code, query.onSnapshot triggers its callback every time there's a change to the result of the query.

  • The first time, the callback is triggered with the entire result set of the query, meaning the whole restaurants collection from Cloud Firestore. It then passes all of the individual documents to the renderer.display function.
  • When a document is deleted, change.type has the value removed. In this case, a function is called that removes the restaurant from the UI.

Now that you've implemented both methods, refresh the app and verify that the restaurants that you saw earlier in the Firebase console are now visible in the app.

Your app is now reading and writing data with Cloud Firestore!

As the list of restaurants changes, this listener will keep updating automatically. Try going to the Firebase console and manually deleting a restaurant or changing its name; you'll see the changes show up on your site immediately.

It's also possible to fetch documents from Cloud Firestore once, using the Query.get() method, rather than listening for real time updates.

So far, this codelab has shown how to use onSnapshot to retrieve updates in real time; however, that's not always what you want. Sometimes it makes more sense to fetch the data only once.

In this section, you'll implement a method that's triggered when a user clicks into a specific restaurant in your app.

  1. Go back to your file scripts/FriendlyEats.Data.js.
  2. Find the function FriendlyEats.prototype.getRestaurant.
  3. Replace the entire function with the following code:

FriendlyEats.Data.js

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

After you've implemented this method, you'll be able to view pages for each restaurant. Just click a restaurant in the list, and you should see the restaurant's details page:

You'll implement adding ratings in the next part of the codelab.