17.
Introduction to Cloud Firestore
Written by Harun Wangereka
In the previous chapters, you learned how to use Realtime Database for storing data in the cloud. Firebase offers another product that you can use for storing data in the cloud: Cloud Firestore.
Cloud Firestore has a similar feature set as Realtime Database. It allows you to store data in the cloud and sync data across devices. It is designed to overcome all the drawbacks of Realtime Database — and it also stores data within a single JSON document.
In this chapter, you’ll learn how to use Cloud Firestore and get familiar with the differences between Realtime Database and Cloud Firestore. More importantly, you’ll learn how to determine when it’s appropriate to use one over the other.
What is Cloud Firestore?
Cloud Firestore is a NoSQL database similar to Realtime Database. It stores data in a structure that looks like a tree, but where data is stored as documents.
Documents and collections are the primary building blocks of Cloud Firestore. It’s helpful to think of documents as files. These files consist of key-value pairs known as fields — this is similar to how models work. The values can be anything - strings, numbers, binary data, or even nested objects in a map format that resembles a JSON object. Collections, on the other hand, are simply groups of documents.
When working with Cloud Firestore, there are a few rules to keep in mind:
- Collections can only contain documents. For example, you can’t add a
Stringobject to the collection. - Documents can’t contain other documents; however, they can point to subcollections. For example, your collections can contain many documents, and those documents can point to other collections. This is how things are formatted in a tree-like structure.
- The root of the Cloud Firestore database can only contain collections.
For example, in the WhatsUp app you created earlier, you could have a Posts collection that contains a document for each post. Each document would point to a Comments collection that contains comments for that post, and the document that contains the comments would point to another collection, and so on.
When you worked with Realtime Database, you learned that you should avoid these deeply nested hierarchy structures. In Cloud Firestore, however, these deeply nested structures are typical because the queries are shallow, meaning that querying data from a document will get you only that document; you don’t have to query the entire collection or the subcollections within the document. This also means that queries are more efficient and flexible than in a Realtime Database, especially when it comes to filtering and sorting the data.
With the WhatsUp app running with Cloud Firestore, you could have a collection of posts and any other collections you need to represent the data.
Cloud Firestore vs. Realtime database
Due to the similarity between Realtime Database and Firestore, you may be wondering how they’re different. Both of these products offer a cloud-based database solution with real-time data syncing for mobile clients, so what gives?
You can think of Cloud Firestore as an improved version of Realtime Database because it’s designed to overcome the drawbacks of the Realtime Database with things like scaling, data structuring and querying. Since Realtime Database stores data as one big JSON tree, it’s challenging to organize and scale complex data.
Firestore has a new and intuitive data model. It handles complex data using subcollections within documents. Because of how it stores documents and data, Firestore has faster queries than Realtime Database, and it supports indexed queries with compound sorting and filtering. Additionally, in Realtime Database, you can’t sort and filter the data in the same query. When you query the data, the result is the whole subtree. Firestore allows all kinds of query chaining that NoSQL databases allow. Instead of querying entire collections or a document, you can query subcollections within a document. Furthermore, in Realtime Database, you need to perform write operations in a single query; in Firestore, you can collect all of your data and write it as a batch operation. This means that it executes one large job in small parts to improve efficiency.
Firestore has a lot of advanced features too. Both Realtime Database and Firestore offer offline support; however, Realtime Database offers it only for mobile clients, while Firestore offers it for web apps as well.
With Realtime Database, you need callbacks for transactions. With Firestore, you don’t. Transactions complete automatically when all of them are finished.
Scalability in Realtime Database isn’t that big of a problem, but when the data exceeds the limits you learned about in Chapter 16, “Usage & Performance”, you needed to shred your data across multiple database instances. In Firestore, you won’t need to do that regardless of how big your database will be — it handles database scaling for you. This is a considerable improvement for large-scale projects.
Firestore also has support for complex queries when the user is offline. Realtime Database only offers support for simple queries of local data.
Like Realtime Database, Firestore is free, up to a certain point; you need to pay for your database to scale. Firestore charges based on the reading and write operations that you’re performing on the database.
Because of the improvements that the Firestore offers compared to the Realtime Database, Firebase recommends using the Firestore for all new projects.
Cloud Firestore Data Structure
In this chapter, you learned that Firestore is a NoSQL database, meaning there is no SQL. But if there’s no SQL, you can’t build queries that will take one piece of data from one part of the database, and another piece of data from another part of the database, and merge them. In Firestore, to get data from two different parts of the database, you must make two different requests. If you run into that scenario, you likely need to re-structure your data in a way that you’ll always be able to get what you need in one request.
You also learned that Firestore Database consists of collections and documents. Take the WhatsUp app, for example. While it’s possible to have a Posts collection that contains individual posts as documents, WhatsUp has the feature where every post can contain comments. Maybe you can make it so that every post document contains a Comments subcollection, and that that collection contains comments for that post. With that setup, you could easily fetch the post and the comments in a single call. However, that’s not how you want to do that.
When you think about it, you don’t need to know about the post comments until the user opens the post by tapping on it. It’s only when the post details screen appears that you need the comments. In this case, a better approach is to have a Comments collection stored as a separate collection rather than as a subcollection. You can then put the post id to the individual comment, so you’ll know to which post the comment belongs. Finally, when fetching comments, you can filter them by the post id and get all of the comments that belong to a specific post.
There is one drawback to this approach, however, and that is data duplication. Every comment has an author so you’ll likely want to know who wrote the comment. In WhatsUp this isn’t the case, but in other apps, you could have another collection of users, and then the comment would need to contain the user data.
By doing that, not only do you fill the database with duplicate user data objects in each of the comments but also if the user chooses to change the data, you’ll need to update all of the comments, as well. So, perhaps a better approach is to store the author id in the comment; then, when you need to get the user data, you can filter out the independent Users collection using the available id.
One significant advantage of the NoSQL database is that it can distribute data across multiple machines easily. In relational databases, when you have an app that’s becoming more popular and needs more storage space, you’d need a more powerful and bigger machine. This is known as vertical scaling.
In many NoSQL databases, including Firestore, when you need more storage, Firestore spreads your data across many servers. This is known as horizontal scaling, and it’s much easier to scale horizontally than vertically. Why? Because it’s much easier to get many moderately powerful machines than to continually upgrade a single machine to handle everything. Machines have their limits too, you know!
Collections and Documents
You learned that Realtime Database stores data as one large JSON tree that contains keys and values. You also learned that these values can be objects containing other key-value pairs. Firestore is a collection of objects that are stored in a hierarchical structure that resemble a tree. Every object in a collection is represented as a document. The document consists of key-value pairs known as fields in Firestore. These values can be strings, numbers, binary data, or nested objects in a map format. The limitation, however, is that the document size must be less than 1MB.
In simple terms, collections are nothing more than a group of documents. A document can’t contain other documents, but it can contain another collection known as subcollection. Collections are containers for documents. A collection can only contain documents. It can’t directly contain raw fields with values, and it can’t contain other collections. The document names within a collection are unique.
The general hierarchy in Firestore is usually collections first, then documents. A document can have a subcollection. Inside a subcollection you have documents and you can have subcollections too. This allows you to nest more data. The nesting limit is 100 levels deep. Subcollections enable you to structure data hierarchically, making data easier to access. Your collections and documents should follow this hierarchy.
Cloud Firestore supports many data types. To learn more about them, visit the official documentation: https://firebase.google.com/docs/firestore/manage-data/data-types.
References
Firestore identifies each document by its location in the database. A reference is a lightweight object pointing to a location in your database. A reference doesn’t perform any network operation. You can create a reference to a location even if it doesn’t have any data. You can create references for your collections and documents. Collection reference and document reference are different types of references. This means the operations on each reference also differ.
Key points
-
Cloud Firestore is a NoSQL database similar to Realtime Database.
-
Firestore stores data as a collection of objects which are stored in a hierarchical structure that resemble a tree.
-
Documents and collections are the main building blocks of Cloud Firestore.
-
Documents consist of key-value pairs known as fields.
-
Collections are a group of documents.
-
Collections can only contain documents.
-
The root of the Cloud Firestore database can only consist of collections.
-
A document can’t contain other documents, but it can contain another collection; these are known as subcollections.
-
It’s easier to query, filter and sort data using Firestore since it can all be done within a single request.
-
It’s best to use foreign-key-like fields in objects, as you don’t want to duplicate data and clutter the database.
-
Firestore scales horizontally; this is easier than Realtime Database which scales vertically.
-
A reference is a lightweight object that points to the location of a collection or document.
Where to go from here?
In this chapter, you learned the basics of Cloud Firestore. You learned what Firestore is, the differences between Firestore and Realtime Database, and how Firestore structures the data. You still have a lot to cover, so be sure to visit the official documentation (https://firebase.google.com/docs/firestore) to understand the specifics of Cloud Firestore better.
In the next chapter, you’ll learn how to manage Firestore data using Firebase Console and how to add and delete data from the database.