Luciano Serruya Aloisi

Firebase, am I right?

Oh, Firebase. Didn't we all fell in love it with when we just knew it? I know I did. Although I think is an awesome toolbox (I'm currently using it in many of my projects), we should also know where and when it works best.

What is Firebase anyway?

Back in 2011 when it was first created, Firebase was a real-time database which allowed users to get updates whenever data changed. In 2014, it was acquired by Google, and nowadays Firebase is a product suite consisting in 19 different products (July, 2020). In case you want to have real-time updates of your data, send push notifications to your users, implement analytics and what have you, Firebase's got you covered.

Firebase's original product still exists, and is actually called Firebase Real Time database (and it's not the only database offered by Firebase that allows you to get real-time data updates). But now Firebase is more than that, is a whole platform.

Because it is now part of Google, Firebase relies heavily on Google Cloud Platform. It depends so much on it that if you create a Firebase project, it automatically creates an underlying GCP project for you to use. You can also go the other way around, creating a GCP project first and then a Firebase project on top of your GCP project.

If you see some of the products Firebase offers, you may see that some of their names start with Cloud, such as Cloud Firestore, Cloud Storage, and Cloud Functions. This means that they are not "exclusive" Firebase products, but GCP products which can be accessed through Firebase. For those cases, Firebase provides Web and Mobile SDKs so you can easily use them from your web or mobile app.

When it comes to Cloud Functions, there is something more to say. To this day (July 7, 2020), you can write your Cloud Functions and deploy them through GCP in JavaScript (Node.js), Python, Go, and they've recently added Java (so you can also write them in any JVM-based language, such as Kotlin, Scala, and Clojure). If you deploy your Cloud Functions through Firebase, you can only choose Node.js (the firebase CLI also allows you to scaffold a TypeScript setup so you can write your code in TS instead of plain JavaScript). If you are writing serverless functions, GCP offers you a broader selection of languages and customization than Firebase. On the other hand, deploying your functions through Firebase is easier because you declare what type of event each function will listen to directly in the code, whereas with GCP you have to provide that information via CLI flags.

As a Backend-as-a-Service provider or a comprehensive app development platform (taken from Firebase's landing page), Firebase allows us to add elements of back-end as needed. Maybe we bootstrapped our web app with a single index.html file and deployed it to Firebase Hosting, then we needed a database, so we integrated Firestore to our app, finally we required to run some code on the back-end or respond to some event, so we added some Cloud Functions.

Golden Hammer

After my first experience of using Firestore to store data for some projects I worked on (and using the Firebase SDK, so hitting Firestore directly from the client app), I thought that we would increasingly stop relying on a back-end infrastructure having such technologies. After some weeks into these projects, I realized that I couldn't be further away from the truth. However, it is true that using Firestore or Real-Time Database you don't need a back-end at all for really simple projects ("simple projects" when it comes to back-end logic or data storage, I mean), but sooner or later they will require some sort of back-end when you want to tackle issues such as security, performance, UX, and so on. Maybe some day we won't require a back-end anymore, but I personally think that Firebase as a data storage provider won't quite make it possible.

Now I'd like to talk a little bit about Firestore. Firestore is a NoSQL document-based database (something like MongoDB) and one of its most interesting features is real-time updates. It was released back in 2017, and it certainly is Firebase's recommendation for a database - the Real-Time database still exists and you can use it, or even use both. This article talks abouts key differences and helps you choose the optimal one for your project.

Instead of having tables and rows like in a SQL database, in Firestore we have collections and documents (documents can also have subcollections). Every document can have one or multiple fields of a certain type (such as string, boolean, number, timestamp, and some complex types such as arrays and maps), and you can have different documents in a single collection with different fields (they don't follow any sort of schema or structure). This flexibility comes as a trade-off over integrity, which you can have but you would have to do it manually (using Cloud Functions or Security Rules). I won't dive deeper into how Firestore works; if you would like to know, I recommend you searching through the Firestore's documentation and watching this amazing video series by Todd Kerpelman.

I would like to mention that every single data field in every single Firestore document in indexed, which means that queries are really fast, even if you are querying a massive collection (I won't explain how an index works or why it makes querying so fast though, sorry). The things is that having every single field indexed restricts how many operators we can use to query data, being only able to query for equal, greater/lesser (or equal) than, and some arrays operations. If you noticed, you cannot query for distinct than (you can work around this limitation by using an array and using the in operator in case you are looking for a discrete value).

Let's now suppose we're using Firestore to build a chat application. Firestore seems like a great choice for this kind of application, as we need to get real-time updates whenever data changes. Our MVP is up and running, but our users request for a search feature, which should allow you to search messages based on a string they give you. In a SQL world, we can very naively implement this feature by using the LIKE operator, like so

SELECT * FROM messages WHERE body ILIKE '%<TEXT>%'

(as far as I know, ILIKE is a PostgreSQL-specific feature)

Such a simple query is impossible to make in Firestore, and in fact there is a whole page in Firestore's documentation talking about this issue and how to solve it using a third-party service such a Algolia (you could also solve it using Elasticsearch).

So you now need to index your messages in a third-party service, and to do so you would add a Cloud Function that triggers when a new document is written to your Firestore collection. Then, you also need some way to communicate with your message-indexing service; maybe you are lucky enough to have a client SDK that allows you to do that from your client app, but if that's not the case (or if you don't want to communicate directly with it from your client app), you also need an endpoint which then hits your message-indexing instance. At the end of the day, you added several pieces of back-end infrastructure to add a single feature just because Firestore doesn't allow you to do a full-text search (and because of how Firestore works, it won't ever be able to do so).

Having said that, my recommendation is to bear in mind Firestore's limitation when choosing a data storage technology, because it'll be really hard to remove it once your app is integrated with Firestore than not using it in the first place (will be even worse if you use any client SDK to hit Firestore directly from your app). A rule of thumb I coined is that if you need to search data in your app, do not use Firestore (at least as your main database; you can of course use it to synchronize data between multiple clients).

What about hosting?

If you want to host you static app or static files, Firebase has a product that meets your needs, and it is actually called Firebase Hosting. It really is an awesome product, because not only it allows you to host your static files, but it also caches your files in CDNs worldwide, so your files will be delivered almost instantly to your users.

If your app does not consist in a single static file (if it is not a Single Page Application, for example), but you need a server instance to render it (a Server-Side Rendered app), Firebase Hosting can also give you a hand. You can serve your app from a Cloud Function or a docker container running on Cloud Run, and rewrite any incoming request from Firebase Hosting to your app.

If you migrated your completely static app to a Server-Side Rendered one and you are still using Firebase Hosting, bare in mind the following: Firebase Hosting gives precedence to any files in your public directory you uploaded, so if you are still uploading a index.html file, that file will be served instead of reaching out to your server to render your app (I talked about this topic in my previous article about Angular Universal). To work around this issue, you would have to avoid uploading any HTML files to Firebase Hosting if you are always rendering your markup on the server (or maybe even upload a empty directory to Firebase Hosting). If you do so, please remember what will happen: as you will be reaching out to your server to render your app every time, it may take some time to start seeing any sort of response (due to the process of rendering your app, and due to cold starts). If you think it twice, maybe you don't want to render your app every single time, but you can cache it in a CDN. To still leverage Firebase Hosting's abilities of caching your content in CDNs worldwide, you will have to manually send the corresponding HTTP headers.

Conclusion

There is not doubt that Firebase is an awesome tool set, but it has its pros and cons. It can be very useful when we are prototyping or scaffolding our app, but it may bring more headaches than solutions in the long run (or it may not, depends on the feature set).

Hope you liked it!

🐦 @LucianoSerruya

📧 lucianoserruya (at) gmail (dot) com