Building a Slackbot Clone using Firebase Realtime Database & Vue.js - Part 1
Reading Time:
Reading Time:
Following is the overview of the various components involed. We use the Firebase Realtime Database for storing the data, Vue.js to render the UI.
This application also includes the Firebase Authentication which you can read about in the previous post Implementing Firebase Authentication. The same implementation of Authentication is used in this application as well. The previous post explains how to create a Firebase application, setting up the Authentication and Authenticating users.
The Firebase Realtime Database is a cloud-hosted database. Data is stored as JSON and synchronized in realtime to every connected client.
The Realtime Database provides a declarative rules language that allows you to define how your data should be structured, how it should be indexed, and when your data can be read from and written to.
Using the Realtime Database we can store the data using a JSON like format. To use the Realtime Database, we need to configure the database URL while initializing the Firebase config by specifying the database URL from the Firebase Console.
It will have this format – https://<databaseName>.firebaseio.com
var config = {
apiKey: "apiKey",
authDomain: "projectId.firebaseapp.com",
databaseURL: "https://databaseName.firebaseio.com",
storageBucket: "bucket.appspot.com"
};
firebase.initializeApp(config);
Once the configuration is setup, we can get the reference to the Realtime Database using firebase.database()
method.
// Get a reference to the realtime database
var database = firebase.database();
The Realtime Database synchronizes with all the clients that is connected to it. Any kind of modification to the data from a client will reflect to all the other clients via events with which the client side code decides what to do corresponding to that event.
Note: By default, the read and write operations are permitted only to Authenticated users. These rules can be changed in Database sections Rules Tab. To know more on how to configure those rules read here.
Any operations like Creating, Updating, Deleting or Listening to changes for a data requires a reference to that particular piece of data, which is an instance of firebase.database.Reference()
.
Consider the following example database structure,
-posts: List
-{postId}
-title
-description
-author
-comments: List
-{comment1Id}
-comment
-{comment2Id}
-comment
The post has few data properties and the comments which is a List(posts is itself a List) of data object. To update a data on post, we would need a reference to the post data and to add a new comment to the comments List, we would need a reference to the comments of the post data item.
To get a reference to a piece of data, we should use the firebase.database().ref()
method. The method must be passed a string construct, which must be constructed by traversing the JSON structure, separated with /
.
So to get the reference to a post,
var postRef = firebase.database().ref('posts/' + postId);
and to get a reference to the comments,
var commentsRef = firebase.database().ref('posts/' + postId + '/comments');
finally to get a reference to a particular comment,
var commentRef = firebase.database().ref('posts/' + postId + '/comments/' + commentId);
Once we get a reference to a data item or List, we can modify and add items to it based on the data property.
First, we need to get a reference to the data item that we need to perform the operation on.
var postRef = firebase.database().ref('posts/' + postId);
var commentsRef = firebase.database().ref('posts/' + postId + '/comments');
var commentRef = firebase.database().ref('posts/' + postId + '/comments/' + commentId);
To save a new record to a reference, use the set()
method.
// Create
postRef.set({
title: 'This is a sample title',
description: 'This is a small description',
author: 'codedodle'
});
var commentRef = commentsRef.push();
commentRef.set({
comment: 'This is a sample comment'
});
Using the set
method, we can add a new data of the referenced item. When the push()
method is used, it creates a new auto generated unique id for the data item and we can use that to set the value for that item. Above we can see how the commentsRef
is used to push and create a new comment and update the value for comment using commentRef
reference.
To update a data item, use the update()
method.
// Update
postRef.update({
title: 'Updated sample title',
description: 'Updated small description',
author: 'codedodle'
});
Passing the updated value to the update
method upon the reference updates the referenced value.
To read a value of a reference, we need to listen to the value event using either the once()
method, if you need the value to be loaded only once and on()
method, if you need the value to be loaded when it is updated.
// Read
// Load only once
postRef.once('value').then(function (snapshot) {
var title = snapshot.val().title;
});
// Load whenever the value is updated
postRef.on('value', function (snapshot) {
var title = snapshot.val().title;
});
To delete a data, you just call the remove()
method on the reference that needs to be deleted.
// Delete
postRef.remove();
Data reference is always not a single object, sometimes we have reference to an List(Array) of data. When it’s a list of data, we can listen to events that updates the client whenever the element from the list is added, removed or modified.
To listen to such event(s) of a List reference, we can use the on()
method. Events such as the child_added, child_removed orchild_changed are triggered whenever the child elements of the list are added, removed or changed.
var commentsRef = firebase.database().ref('posts/' + postId + '/comments');
// Triggered whenever a new child is added to the list.
commentsRef.on('child_added', function(data) {
addCommentElement(postElement, data.key, data.val().text, data.val().author);
});
// Triggered whenever a child is changed.
commentsRef.on('child_changed', function(data) {
setCommentValues(postElement, data.key, data.val().text, data.val().author);
});
// Triggered whenever a child is removed from the list.
commentsRef.on('child_removed', function(data) {
deleteComment(postElement, data.key);
});
Here we are going to look into the database structure(JSON Tree) of the application we are building. There are some best practices(like Avoiding Nesting of data, Creating flat data structure and scaling) for designing database structure for the Realtime Database which you can read it here.
We need to be able to store two things in the database,
To store the Individual User Messages we’ll have the following format.
-chats: List
-{userId}
-messages: List
-{messageId}
-cmd: One of the available commands
-m_type: [cmd | msg]
-text: Text to be displayed in message
-timestamp: Message Created Timestamp
-data: Object - Varies with command
-{messageId}
-cmd: One of the available commands
-m_type: [cmd | msg]
-text: Text to be displayed in message
-timestamp: Message Created Timestamp
-data: Object - Varies with command
-{userId}
-messages: List
-{messageId}
-cmd: One of the available commands
-m_type: [cmd | msg]
-text: Text to be displayed in message
-timestamp: Message Created Timestamp
-data: Object - Varies with command
Available commands have the following format.
-commands:
-commandName:
-cmd: The Command i.e "/{commandName}"
-desc: A small description of the command
-commandName:
-cmd: The Command i.e "/{commandName}"
-desc: A small description of the command
Using the firebase realtime database APIs discussed above, following is a utitlity written to Add, Remove and Update a message for the currently logged in User.
/**
* A small wrapper utility around the firebase database for the
* application's messaging functionality.
*/
var fireUtil = {
/**
* Add a new message into the firebase realtime database
*
* @param Object data
* @return boolean
*/
addMessage: function addMessage(data) {
if (firebase.auth().currentUser === null && data !== null) {
return false;
}
var user = firebase.auth().currentUser,
messagesRef = firebase.database().ref('chats/' + user.uid).child('messages');
newMsgRef = messagesRef.push();
newMsgRef.set(data);
return true;
},
/**
* Remove a message from the firebase database for the current user.
*
* @param String msgId
* @return boolean
*/
removeMessage: function removeMessage(msgId) {
if (firebase.auth().currentUser === null && msgId !== null && msgId !== '') {
return false;
}
var user = firebase.auth().currentUser,
messageRef = firebase.database().ref('chats/' + user.uid + '/messages/' + msgId);
messageRef.remove();
},
/**
* Update a message in the firebase database for the current user.
*
* @param String msgId
* @param Object data
* @return boolean
*/
updateMessage: function updateMessage(msgId, data) {
if (firebase.auth().currentUser === null
&& msgId !== null && msgId !== '' && data !== null) {
return false;
}
var user = firebase.auth().currentUser,
messageRef = firebase.database().ref('chats/' + user.uid + '/messages/' + msgId);
messageRef.update(data);
}
};
Next – Building slack bot clone using Firebase and Vue.js – The Bot – Part 2