If you’re a developer who’s written any applications other than a Hello World, chances are you need to persist data. And very often, you need to store the data on a database server so that the data can be synchronized across a wide range of devices. However, setting up a back-end database server isn’t a walk in the park, especially for developers who are already grappling with the business logic and the various ever-changing front-end technologies. Fortunately, you don’t have to set up your own database server just for that purpose. You can use the Cloud Firestore service from Google, which allows you to store your data on the cloud, thereby allowing you to focus on building responsive apps that are able to work both online and offline.

In this article, I’ll walk you through the use of the Cloud Firestore through a React Native application. I won’t go into the various specific features of Cloud Firestore because my focus is on getting you started. To get the most out of this article, you should be familiar with JavaScript, but most people should have no problem in following along.

What is Cloud Firestore?

Cloud Firestore is a NoSQL document database that’s designed for your client- and server-side apps. It’s part of Google’s Firebase suite of services, which includes services like Cloud Functions (for running serverless apps), Hosting (for hosting your Web apps), Authentication (for you to authenticate users in your apps), and more.

Using Cloud Firestore, you can save your data on the cloud and at the same time keep your clients synchronized through real-time listeners. It also provides support for offline access, which is especially useful for mobile apps.

Basic Concept of NoSQL

Unlike traditional relational databases where data are stored in table-based formats, NoSQL has the concept of documents. Let’s start with the most basic. To represent your data, you use key/value pairs (see Figure 1). For example, you can represent the age of a user using age:25, and the name of the user using name:"Wei-Meng Lee".

Figure 1: Storing data as a key/value pair

All of these key/value pairs can then be grouped together, forming a Document (see Figure 2).

Figure 2: Multiple key/value pairs form a document

A collection of these documents is called, as you guessed, a Collection (see Figure 3).

Figure 3: Multiple documents form a collection

Unlike relational databases where every row in a table has the same fields, a NoSQL database can have a collection of documents, each with its own unique structure, without needing to predefine the structure.

Figure 4 shows a collection of three documents, each with its own structure.

Figure 4 : Documents in a collection need not have the same structure and fields

In addition, you can add fields to a document as you go, giving you maximum flexibility in the way you store your data. Besides storing the data in a document, a document itself may reference another document or collection. Figure 5 shows this visually.

Figure 5 : A document may reference another document or collection

Logging In to Firebase

Now that you have a good idea of how the data model of NoSQL works, it’s the perfect time to jump into Cloud Firestore and create your first project.

Using a browser, navigate to https://firebase.google.com. Click GET STARTED (see Figure 6).

Figure 6 : Getting started with Firebase

Click Add project (see Figure 7).

Figure 7 : Creating a new Firebase project

Name the project and check the options as shown in Figure 8. Once it’s done, click Create project.

Figure 8 : Naming the project

When the project is created, click Continue. You should now see the screen as shown in Figure 9.

Figure 9 : The Firebase console

Click the third icon (</>) to add Firebase to your Web project (see Figure 10).

Figure 10 : You can add Firebase to your different types of projects: iOS, Android, and Web

You should now see the credentials (see Figure 11) to access Firebase from your Web project. Save the information to a text file. You’ll need it later.

Figure 11 : The credentials you need to use to use Firebase in your Web app

Click on Database in the Firebase project console and then click Create database (see Figure 12).

Figure 12 : Creating a database in Cloud Firestore

Select the Start in test mode option and then click Enable (see Figure 13).

Figure 13 : Starting the database in test mode for testing purposes

Choosing the "Start in test mode" option allows you to read and write to your database. For real-world usage, you need to set the necessary authentication options to ensure that your database is properly secured.

Creating the Project

For this article, you’ll use React Native to create a cross-platform mobile app to use Cloud Firestore. To get you started, you’ll learn how to perform the following in Cloud Firestore:

  • Subscribe to document change notifications so that changes to the collections in Cloud Firestore notifies all clients about the changes
  • Create a document and obtain its document reference ID
  • Retrieve a document based on its document reference ID
  • Retrieve all of the documents stored in a collection
  • Perform queries to retrieve specific documents
  • Modify a document based on its document reference ID
  • Delete a document based on its document reference ID

In Terminal, type the following command to create a React Native project:

$ react-native init Demo

Once the project is created, type the following command to install the firebase module using npm:

$ cd Demo
$ npm install firebase --save
$ touch config.js

Populate the config.js file with the following:

import * as firebase from 'firebase';
import firestore from 'firebase/firestore'
const settings = {timestampsInSnapshots: true};
var config = {
  apiKey : "xxxxxxxxxxxxxxxxxxxxxx",
  authDomain : "xxxxxxxxxxxxxxxxxxxxxx",
  databaseURL : "xxxxxxxxxxxxxxxxxxxxxx",
  projectId : "xxxxxxxxxxxxxxxxxxxxxx",
  storageBucket : "xxxxxxxxxxxxxxxxxxxxxx",
  messagingSenderId : "xxxxxxxxxxxxxxxxxxxxxx"
};
firebase.initializeApp(config);
export default firebase;

The config.js file contains the details needed to access the database on Cloud Firestore. Replace the various values with the credentials you obtained earlier.

Add the statements in bold to the App.js file (see Listing 1).

The following statement creates a reference to the collection named ‘courses’ located in the Cloud Firestore. Using this reference, you’ll be able to manipulate the collection, such as add, modify, delete, and query for documents:

    this.ref =
      firebase.firestore().collection('courses');

Using Xcode, open the iOS project located in the Demo folder. Run the application on the iPhone Simulator. If there’s no error, you should see the default UI as shown in Figure 14.

Figure 14: The iOS app running on the iPhone Simulator

Creating the User Interface of the Application

Let’s now populate the UI of the iPhone application with the necessary views so that users can enter details to be saved in Cloud Firestore.

Add the statements in bold to the App.js file as shown in Listing 2. Specifically, you are adding the following views to the UI:

  • Button
  • TextInput
  • Text

For this project, you’ll allow users to enter details of courses to store in the Cloud Firestore. Each course has the following details:

  • Course code
  • Course title

You’ll use four state variables to bind to the UI:

this.state = {
  id : "", // to store the
                    // document reference ID
  coursecode : "", // store the course code
  coursetitle : "", // store the course title
  courses : "" // store the list of courses
};

Refresh the iPhone Simulator (press Command-R) and you should now see this the UI as shown in Figure 15.

Figure 15: The app with the updated UI

In Xcode, run the application on another iPhone Simulator (see Figure 16).

Figure 16: Running the app on another simulator instance

I want to launch the app on two instances of the iPhone Simulator so that in the next section, you’ll see that changes made to a document in Cloud Firestore cause an update to be pushed automatically to all the apps listening for the update.

Subscribing to Document Changes

Now that you have all of the UI set up, let’s start by seeing how apps can subscribe to changes in the Cloud Firestore.

Add the statements in bold to the App.js file as shown in Listing 3. To subscribe to updates, use the onSnapshot() function from the collection object and pass it a function to handle the event:

//---subscribing to collection updates---
this.ref.onSnapshot(this.onCollectionUpdate);

In this example, you’re subscribing to changes to the courses collection. Any time there’s any change made to the collection, the onCollectionUpdate() function is called:

  //---fired whenever someone adds a document to
  // the collection---
  onCollectionUpdate = (querySnapshot) => {
    courses = "";
    querySnapshot.forEach((doc) => {
      courses += doc.data().CourseCode + "-" +
                 doc.data().CourseTitle + "\n";
    });
    //---update the state so that the courses
    // can be refreshed---
    this.setState(
      {
        courses: courses
      }
    );
  }

For this implementation, you’ll iterate through the snapshot and show the list of courses added to the collection so far.

Refresh the iPhone Simulator.

Adding a Document to the Collection in the Firebase Console

Back in the Firebase console, click the Add collection link as shown in Figure 17.

Figure 17: Adding a collection to the Firestore database

For the Collection ID, enter courses. Click Next (see Figure 18).

Figure 18: Naming the collection

Enter CourseCode for the first field and CourseTitle for the second field. Set their values as shown in Figure 19. Click Save.

Figure 19: Naming the fields in a document

Leave the Document ID empty so that Firestore generates the ID for you automatically. The document is now created (see Figure 20).

Figure 20: The newly created document

Observe that the newly added document now automatically appears on both simulators (see Figure 21).

Figure 21: Both simulator instances showing the newly added document

Back in the Firebase console, click the icon with the three dots (displayed in the second column next to the courses title) and select Delete all documents (see Figure 22).

Figure 22: Deleing all documents in the collection

Click Start delete (see Figure 23).

Figure 23: Confirming the deletion of the document

This will delete the document that you have just created. The iPhone simulators now automatically removes the document and no longer shows it.

Adding a Document

You have seen how to add a document to a collection in the Firebase console, so let’s now see how it’s done in code.

Add the statements in bold to the App.js file as shown in Listing 4.

To add a new document to the collection, use the add() function and pass in the content of the document to add. When the document is added successfully, it returns a document reference ID (which in this case is automatically generated by Cloud Firestore):

  addCourse() {
    //---add a new document to the collection---
    this.ref.add({
      CourseCode : this.state.coursecode,
      CourseTitle : this.state.coursetitle,
    })
    .then((docRef) => {
      //---update the id---
      this.setState(
      {
        id:docRef.id
      });
    })
    .catch(function(error) {
      Alert.alert("Status",
        "Error adding document: " + error);
    });
  }

For this project, you will keep the document reference ID in the state variable so that it can be displayed at the top of the page.

Refresh the iPhone Simulator. Enter the information as shown below and click the Add Course link. When the document has been added successfully, you’ll see the document ID displayed at the top of the page (see Figure 24).

Figure 24: Once a document has been added, the document reference ID will be shown at the top of the screen

Retrieving a Document

To retrieve a specific document, you need to use its document reference ID.

Add the statements in bold to the App.js file as shown in Listing 5. To retrieve a specific document, use the doc() function and pass it the document reference ID. When the function returns, you need to check if the document exists using the exists property. If the document exists, you then use the data() function to get the content of the document:

   getCourse() {
    //---exit function if id is empty===
    if (this.state.id == "") return;
    //---get a reference to the document---
    var docRef = this.ref.doc(this.state.id);
    docRef.get()
    .then(function(doc) {
      if (doc.exists) {
          Alert.alert(doc.data().CourseCode,
                      doc.data().CourseTitle);
      } else {
          //---doc.data() will be undefined---
          console.log("Can't find document");
      }
    })
    .catch(function(error) {
        console.log("Error getting document: ",
          error);
    });
  }

Refresh the iPhone Simulator. Add a new document by entering the information as shown in Figure 25 and click the Add Course link. When the document has been added successfully (when you see the document ID displayed at the top), click the Get Course link. You should see an alert showing the document that you have just added.

Figure 25: Retrieving the document just added using its document reference ID

Retrieving All Documents

If you want to retrieve all the documents in a collection, you can simply use the get() function. Add the statements in bold to the App.js file as shown in Listing 6. Once the documents are fetched, you can iterate through each of them individually:

  getAllCourses() {
    this.ref
    .get()
    .then(function(querySnapshot) {
      str = "";
      querySnapshot.forEach(function(document) {
        doc = document.data();
        str += document.id + "\n" +
          doc.CourseCode + " - " +
          doc.CourseTitle + "\n\n";
      });
      Alert.alert("All Courses", str);
    });
  }

Refresh the iPhone Simulator. Click the Get All Courses link and you should see all the courses as shown in Figure 26.

Figure 26: Retrieving all the documents in the collection

Performing Queries

Earlier, you saw how to retrieve a document based on its document reference ID. However, sometimes you don’t have the ID and you need to perform a query to retrieve the relevant documents.

Add the statements in bold to the App.js file as shown in Listing 7.

In this example, you want to retrieve all the courses that have the course code starting with the word "IOT". You use the where() function to specify the condition for the query:

  getAllIOTCourses() {
    this.ref
      .where("CourseCode", ">=", "IOT")
      .where("CourseCode", "<", "IOU")
      .get()
      .then(function(querySnapshot) {
        str = "";
        querySnapshot.forEach(function(document)
        {
          doc = document.data();
          str += document.id + "\n" +
            doc.CourseCode + " - " +
            doc.CourseTitle + "\n\n";
        });
        Alert.alert("All IoT Courses", str);
      });
  }

Refresh the iPhone Simulator. Go ahead and add a few more documents. Click on the Get All IoT Courses link and you will see all courses that has course code starting with the word "IOT" (see Figure 27).

Figure 27: Retrieving specific documents from the collection

Updating a Document

To update a document, you need the document reference ID of the document.

Add the statements in bold to the App.js file, as shown in Listing 8.

To modify a document, you first retrieve the document using the doc() function. You can then update the document using the update() function:

  modifyCourse() {
    //---exit function if id is empty===
    if (this.state.id == "") return;
    
    //---get a reference to the document---
    var doc = this.ref.doc(this.state.id);
    doc.update(
      {
        CourseCode : this.state.coursecode,
        CourseTitle : this.state.coursetitle,
      }
    )
    .then((document) => {
      Alert.alert("Document modified");
    })
    .catch(function(error) {
      console.error(
        "Error modifying document: ",
        error);
    });
  }

Refresh the iPhone Simulator. Add a new document by entering the information as shown below and click the Add Course link. When the document has been added successfully (when you see the document ID displayed at the top), make some changes to the course title and click the Modify Course link. When the changes have been successfully applied, you’ll see it updated automatically in the list (see Figure 28).

Figure 28: Updating a document

Deleting a Document

Finally, you’ll learn how to delete a document.

Add the statements in bold to the App.js file, as shown in Listing 9. To delete a document, get a reference to it using the document reference ID, and use the delete() function:

  deleteCourse() {
    //---exit function if id is empty===
    if (this.state.id == "") return;
    //---get a reference to the document---
    var doc = this.ref.doc(this.state.id);
    doc.delete()
      .then((document) => {
        Alert.alert("Document deleted");
        //---update the state---
        this.setState(
          {
            id : "",
            coursecode : "",
            coursetitle : ""
          }
        );
      })
      .catch(function(error) {
        console.error(
          "Error removing document: ",
          error);
      });
  }

Refresh the iPhone Simulator. Add a new document by entering the information as shown in Figure 29 and click the Add Course link. When the document has been added successfully (when you see the document ID displayed at the top), click the Delete Course link to delete the document.

Figure 29: Deleting a document

Summary

In this article, you learned the basics of the Cloud Firestore, which is one of the various services in Firebase. Using Cloud Firestore, you can store your data on the cloud without needing to worry about hosting your own back-end database—all of the services you need are taken care of. There’s much more to learn about Cloud Firestore, but hopefully this article got you on the right track. Let me know how you are using Cloud Firestore.