Structure Firestore (Firebase) For Scalable Chat App

Hoang Minh Bach
Level Up Coding
Published in
7 min readAug 3, 2020

--

Nowadays, messenger apps are very widespread and everyone uses plenty of popular Chat App such as Facebook Messenger, Whatsapp, Viber, Telegram, and so on. However, the user has many choices to connect to the others, the reasons why many businesses built their own chat apps is because they concerned about security or privacy, and it is needed to manage conversation of their staff or require facilitation without switching many other applications to make conversation.

What is Firebase

Firebase is Google’s mobile platform that helps you quickly develop high-quality apps and grow your business.

Firebase provides a cloud platform that pays for what you use. It means instead of building your own server to store your data, optimize and maintain your hardware, make effort to scale up your system when the number of users has increased, you are able to use Firebase to resolve all of the above issues with reasonable expense. In addition, on this subject matter of Chat App, Firebase offers 2 cloud base databases which are Realtime Database and Firestore:

Cloud Firestore is Firebase’s newest database for mobile app development. It builds on the successes of the Realtime Database with a new, more intuitive data model. Cloud Firestore also features richer, faster queries and scales further than the Realtime Database.

Realtime Database is Firebase’s original database. It’s an efficient, low-latency solution for mobile apps that require synced states across clients in realtime.

The reasons why you should choose Firestore is:

  • Client-first SDKs, with no servers to deploy and maintain
  • Realtime updates
  • The free tier, then pay for what you use

Furthermore, maybe you just want to try something new and see how it is.

Cloud Firestore is a cloud-hosted, NoSQL database that your iOS, Android, and web apps can access directly via native SDKs. Cloud Firestore is also available in native Node.js, Java, Python, Unity, C++ and Go SDKs, in addition to REST and RPC APIs.

Structure of the Database

About to be mentioned Firebase is a NoSQL data model, you store data in documents that contain fields mapping to values. These documents put into collections that are used to organize your structure and build queries. The Cloud Firestore data model supports whatever data structure works best for your app. So far that your database is simply a large JSON object.

Definition of collection

Before the database and the collection is created, we need to create a project and database in Firebase, else, there is a basic guild to start with Chat App using Firebase.

Enable Google Authentication in Firebase

First of all, collection of a user is mandatory, you can store data of a user in Firebase or not, data of the user can be stored in your database using Oracle, PostgreSQL, MySQL, SQLServer, and so on, and just using your userId or something like that to identify the user in Firestore. If you store data of users in your own database on your server or the other cloud platform, you have to assure the availability and the connectivity of your server. To simplify to everyone, I will create a user’s collection to store user data when users sign in via Google Authentication.

To enable Google authentication in Firebase, we go to “Authentication” to left side navigation and choose the “Sign-in method”, after clicking in pencil on the right side of “Google” and enable it. Now we can sign in Firebase via Google Authentication.

Collection of User

When a user signed in the Firebase platform, each user data should be saved in the collection and we just save some basic information of the user, and user unique id is used as a key of the document. The structure of this collection will express something like this:

User Collection

Creating User collection with basic detail such as uid, email, photoUrl, displayName (depend on your requirements).

In this case, gathering information on a signed-in user is not a problem. The below sample can help you sign in via Google Authenticate and get information of users after sign in using Javascript.

const auth = firebase.auth()
...
signInWithGoogleAuthentication() {
const provider = new firebase.auth.GoogleAuthProvider()
return new Promise((resolve, reject) => {
auth
.signInWithPopup(provider)
.then(function (result) {
resolve(result.user)
})
.catch(function (error) {
reject(error)
})
})
},

This is a sample of saving the information of the user to Firestore:

const db = firebase.firestore()
...
saveUserToFirestore(user) {
const userRef = db.collection('user')
userRef.doc(user.uid).set({
uid: user.uid,
displayName: user.displayName,
photoURL: user.photoURL,
email: user.email,
})
},

Next, we need to take care of group chat because one user can join multiple groups and one group will contain multiple users. So that we should create “Groups” in the user’s collection to optimize queries and avoid to take longer than expected to get results back from a complex query.

Collection of group

The collection of groups will store data of who created the group, type of group, members of the group, name, and so on.

Group collection

We should save “userId” to “createdBy” and save “members” as an array of “userId”. “CreateAt” should be auto-generated and the last one is the type which is quite important because it differences between private chat and group chat. Group chat can include multiple members but private chat just contains 2 people so that we use “type” to separate in frontend or control workflow which is conditional on your requirement. Besides, we can create other fields such as “name”, “recentMessage” and so on, and we can check who read this message by the “readBy” field.

Collection of message

We will store all of the messages to this collection which is distinguished by group id. The mandatory field of a message is “sentBy” which stores “userId” and “messageText”.

Message collection

The collection of a message contains documents which have ID is same with Group ID and each document have their collection call messages

Message document
Fields of message
Structure of message collection
Structure of message collection

Query example

Before going to example of a query, you need setup and your app with the following link.

This code below is a sample of queries with Firestore using Javascript which maybe helps you understand more about this structure.

Get an array of a group by user-id

const db = firebase.firestore()
...
fetchGroupByUserID(uid) {
const vm = this
return new Promise((resolve, reject) => {
const groupRef = db.collection('group')
groupRef
.where('members', 'array-contains', uid)
.onSnapshot((querySnapshot) => {
const allGroups = []
querySnapshot.forEach((doc) => {
const data = doc.data()
data.id = doc.id
if (data.recentMessage) allGroups.push(data)
})
vm.groups = allGroups
})
})
},

Filter group using an array of user

const db = firebase.firestore()
...
filterGroup(userArray) {
const vm = this
vm.groups = []
return new Promise((resolve, reject) => {
let groupRef = db.collection('group')
userArray.forEach((userId) => {
groupRef = groupRef.where('members', '==', userId)
})
groupRef
.get()
.then(function (querySnapshot) {
const allGroups = []
querySnapshot.forEach((doc) => {
const data = doc.data()
data.id = doc.id
allGroups.push(data)
})
if (allGroups.length > 0) {
resolve(allGroups[0])
} else {
resolve(null)
}
})
.catch(function (error) {
reject(error)
})
})
},

This above code is used to check private chat (group contains only 2 members), if it has not existed, we should create new, I will show a demo at the end of this story.

Fetch message by group ID

const db = firebase.firestore()
...
fetchMessagesByGroupId(groupId) {
const vm = this
db.collection('message')
.doc(groupId.trim())
.collection('messages')
.orderBy('sentAt')
.onSnapshot((querySnapshot) => {
const allMessages = []
querySnapshot.forEach((doc) => {
if (doc) allMessages.push(doc.data())
})
vm.messages = allMessages
})
},

Save message

const db = firebase.firestore()
...
saveMessage(messageText, sentAt, currentGroupId) {
if (messageText.trim()) {
const message = {
messageText,
sentAt,
sentBy: this.user.uid,
}
return new Promise((resolve, reject) => {
db.collection('message')
.doc(currentGroupId)
.collection('messages')
.add(message)
.then(function (docRef) {
resolve(message)
})
.catch(function (error) {
reject(error)
})
})
}
},

Demo Chat App

Login

Login

Private Chatting

Private chatting

Creating a group

Creating a group

Group chatting

Group chatting

More details about it are located at:

Conclusion

There is a structure of chatting database and I think it is quite simply easy to understand with everyone who wants to research Firestore or chat database structure. Thanks for reading and if I made any mistake, welcome to feedback.

--

--