Real-time Group Chat using Firebase Realtime Database
Today, let us show you how you (yes, just you!) can add a full-featured chat functionality to your Web, Android, or iOS app, in a day’s work. (Spoiler for the backend developers: there’s no backend code needed. Absolutely. Zero. Backend. Code.)
What is Firebase?
Firebase was initially a product developed by a startup named Envolve Inc., that sought to make an API for embedding real-time chat in mobile and web apps. Long story short, it grew wildly and this lead the developers to separate the chat feature and the underlying real-time engine that powered it; and that was eventually acquired by — none other than — Google.
Google took it to a whole new level by integrating its slew of services and features targeted towards mobile developers and evolved Firebase from just a real-time database to “BaaS” – Backend as a Service. I’ll not go much into this but here’s a screenshot from their page with the full arsenal of the services provided —
Firebase Real-time Database
The pièce de résistance
The Firebase site claims that you can use Realtime Database service to “Store and sync app data in milliseconds”. What this means is that your database is essentially a centrally located huge JSON tree, and any changes to any part of it are instantly propagated to all those observing the database. Pause for a moment and let this sink in. Think how powerful it is. And the possibilities for building things with this. Let us also look at some other features this service offers:
- Build serverless apps: Realtime Database ships with mobile and web SDKs so you can build apps without the need of servers.
- Optimized for offline use: When your users go offline, the Realtime Database SDKs use local cache on the device to serve and store changes. When the device comes online, the local data is automatically synchronized.
- Strong user-based security: The Realtime Database integrates with Firebase Authentication to provide simple and intuitive authentication for developers.
Let’s Get Building!
Code > Talk
So, let’s fire up a Firebase Database of our own and see what it’s capable of. We are aiming to build a bare-bones live message board-esque app, which will allow all the users viewing it to see the messages everyone is posting, and also post own messages. We will be building this app in React using
create-react-app, as that’s what we are familiar with, at CauseCode. However, you can select the framework of your choice, opt for iOS/Android, or even go plain vanilla JS.
Step 1: Add Firebase To Your Project
firebase.database() API to interact with our database. Cakewalk, right?
Step 2: Planning The Data Structure
You might be wondering why we have “JSON” and “planning the data structure” in the same article. Well, Firebase Realtime DB does grant you total freedom and power to structure data as you want and then change it at will (eg.: It allows nested data items, up to 32 levels deep), but there are some best practices and dos and don’ts for deciding how your data will go in the database. We will talk about it briefly here, but you can always visit https://firebase.google.com/docs/database/web/structure-data and dive deep if you wish.
Don’t nest data! 32 levels of nesting is a lot and will probably suffice for most needs, but that does not mean you should go ahead and base your entire data structure on a highly nested data model. This is for two reasons:
- In case you need a small piece of information for a number of records that is nested deep somewhere in them, you need to inevitably download all of the records in your client. Which translates to a lot of wasted time, power, and data.
- Firebase Security Rules are mini-policies that can be applied to data items in the database to give a fine-grained control over who can access what part of the database. If you nest too much, and provide someone access to an object at a fairly top level, they end up getting access to all of the nested data inside. Whether they need it or not.
Create flat data structures. This is in continuation to above. Let’s say we are building a chat app which stores chats, members, and messages in the databases. You may be tempted to store the chats at top level in the database, then nest the messages in between them, and each message contains the member who sent it. While fairly intuitive and straightforward, this will lead to the problems described above.
The solution to this: denormalize your data and create references. Extending the above examples, you could create 3 top level objects: chats, members, and messages:
chatswill contain the list of chats and their information
memberswill contain the chats and a list of members participating in them
messageswill contain a list of chats, and the messages sent in that chat
Step 3: Firebase Database References
Recall that our “database” is nothing but a large JSON tree, initially set to null. With that in place, let’s see what References are. Firebase references are nothing but pointers to a place somewhere in the data tree. It could be pointing to the root, or to a very nested data object. But in essence, they’re just pointers to the data locations, allowing us to manipulate the referenced location. How to obtain pointers? Good question. It is very straightforward and intuitive too. Like the Firebase database, what else is structured like a tree? Files and folders you say? You got it! And how do you access them? Bingo! You’re right again, you access them using simple paths. (C:\Windows\System\…).
Similarly, the path
/ in Firebase DB points to the root.
/messages, as you must have guessed, points to an object named messages just under the root. Allow me to illustrate this to let you get a better picture:
So, to access the message “Hii” (the first one), we need to access the path:
/messages/394/<that ugly ID>/message. This is just a sample structuring of data; you can let your imagination run wild and create a data structure to your liking.
Getting a ref in your code: All this is nice, but how do I use it in my code you ask? Well that’s easy too: to get a hold of a reference in your code, just write:
Now, messageRef is literally a reference to the live, online object sitting in your database, and performing operations on it will instantly be reflected in the database as well as all the users listening to it. Making a chat app now sounds a lot simpler, doesn’t it?
Step 4: Writing To The Database
We saw references and how we can use them point them to objects in the database and with that all set, now let us see how to use them (i.e. the `messageRef`) object to perform write operations on the database.
reference.set(value): This operation sets the value present at the path represented by
reference, to the value passed into it. If the value already exists, it replaces it. If the path does not exist or partially exist, it creates the path and sets the value. What this is useful for is when you want total control over naming the keys that refer to your data: Just get a reference to the desired location and key, and
.set(value) on it to put your data at that place.
As an example, doing the following
On an empty database, would yield us with this structure:
reference.push(value): This is similar to
.set(), but what it does is creates another key inside the object pointed to by
reference, and then sets that value of that key to the value passed.
As an example, doing the following:
On an empty database, would yield us the following:
Yes, that’s how the ugly ID you saw in the earlier example was generated.
Step 5: Reading From The Database
Now, with all that data safely stored inside our database, we need to be able to listen to it and display it to our users. Here, too, database references play a vital part. Basically, we attach an event listener to the reference which represents the part of the database we want to listen to, and then that event handler is called whenever that reference or any of its children is altered/added to/removed. Sounds simple? It is! Here’s how it looks:
Would print the entire message object on the console whenever any message is added/removed/altered. If you want a static copy of the data and don’t want to listen to it, we got you covered: just use
.once() instead of .on.
At this point, I am trying my best to not make this section look tiny, but I am really out of content as that is all there is to reading data from the database. Feels good to have all these powerful abstractions distilled in such simple API calls, isn’t it?
So, I put together a small React app as described earlier and as expected, things went smooth. Infact, I spent more time dealing with the UI and React than the actual business logic. Here’s a glimpse of it:
After a simple registration screen asking your name, you directly jump into the chat room:
There can be as many users chatting simultaneously as you want, only limited by the limits set by Firebase (which to be honest, only a few apps will ever reach). Also, any user jumping into the chat at any point in time will be able to see the entire chat history since the chat started. And, of course, messages sync blazing fast across all users.
You can access the sample app at http://causecode-firebase-chat.surge.sh/. Go ahead, give it a spin and see for yourself. It’s not perfect; there are some rough edges as it is basically meant to be a demo of how quickly an app can be got up and running from scratch using Firebase.
You can access the source code at https://github.com/causecode/react-firebase-chat, If you need any help implementing this, or are just curious.
And that’s all for this blog. We got acquainted with what Firebase is, learnt the bare minimum concepts required to use it and create something useful, and went ahead and created a sample app. However, there are some more factors that should be considered when deploying an app like this to production. They are out of scope for this article; however I will list them here for the sake of completeness.
- Secure your database using fine grained control provided by Firebase Database rules: https://firebase.google.com/docs/database/security/
- Working with lists of data, sorting and filtering: https://firebase.google.com/docs/database/web/lists-of-data
- Index data for better performance: https://firebase.google.com/docs/database/security/indexing-data
- Enable offline capabilities and client side caching: https://firebase.google.com/docs/database/web/offline-capabilities
That’s it for this blog. We hope you enjoyed it and were convinced how easy it is to get a full featured chat up and running, once you integrate Firebase Database in your app 🙂
About CauseCode: We are a technology company specializing in Healthtech related Web and Mobile application development. We collaborate with passionate companies looking to change health and wellness tech for good. If you are a startup, enterprise or generally interested in digital health, we would love to hear from you! Let's connect at email@example.com