Introduction

Join me on the journey of developing applications using fullstack and serverless architecture. I will primarily use Vue.js for the front-end while Firebase will be for the database, hosting, back-end logic and file store. So in this post, we will build an expense tracking application that sums up all the amount of each entry.

Back in 2012, I developed a real-time chat application that was embedded on all enterprise applications used by the company I work for that time. The chat app enabled the employees to communicate even on different applications. I used PHP for the back-end and a spaghetti of JQuery in the front-end.I used Ratchet, a PHP Websocket library to handle the real-time side. The solution worked but operation and maintenance is hard. Websocket disconnects, PHP daemons dies, monitoring sucks. In short, handling websockets manually is tough.

Enter Firebase

Now, we have Firebase that lets you build real-time applications without managing any infrastructure. Firebase lets you focus on building your app. You don’t have to build your own back-end infrastructure because Firebase will be your back-end. You can now have a REST API, a database, a file storage, an event management, an authentication handler. It is also integrated with Google Cloud platform in case you have other computing requirements or else you have superseded Firebase’s capabilities.

In this tutorial, we will use Firebase together with Vue.js to build an expense tracking app. So, open your terminal and let’s get started!

Create Vue.js project

First, make sure you have Node.js and NPM installed on your local system. Next, to generate Vue.js projects, make sure that Vue CLI is installed. If not, you can install it like this:

npm install -g @vue/cli

We will create a project using the cli. The cli will bootstrap an application using a the default preset. You can also manually select features that you want to be included in the application.

vue create expenses

Select default for now, we will deal with manually selecting features on future tutorials.

The process of initializing a new project will take a few minutes. Have a pee break and come back after. Once the initialization is done, do this commands

cd expenses
npm run serve

That will fire up the built-in web server and host your application. You can access it by going to http://localhost:8080. The initialized project will look something like this:

From HelloWorld.vue file

Let’s go back to your code editor of your choice (I highly recommend Visual Studio Code) and open src/App.vue. Delete the predefined code. Replace it with this:


<template>
    <div id="app">
        <HelloWorld/>
    </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
    name: 'app',
    components: {
        HelloWorld
    }
}
</script>

Next, open src/components/HelloWorld.vue and replace the predefined code with this:

<template>
    <div>
        
    </div>
</template>

<script>
export default {
    name: 'HelloWorld',
}
</script>

Save App.vue and HelloWorld.vue. Vue CLI will hot reload the application and you can now see the result in the browser. This will be blank for the meantime.

Create Firebase project

Sign up to Firebase and create a new project. Name your project whatever you like. Once done, go to your project and proceed to the Database menu. Create a database using Cloud Firestore. Cloud Firestore allows us to have a real-time NoSQL Document Database that syncs with all connected clients.

Cloud Firestore

Start in test mode for now though it is not advisable leave it in production. Use the locked mode when in production environment.

Go back to the project overview and click Add App. Choose the web platform and keep a copy of this part:

Let’s start coding

Now that we have our Cloud Firestore initialized, let’s add Firebase to our application. Go back to your terminal and close the server using CTRL + C. Enter this command to add the firebase package:

npm install firebase vue-firestore lodash --save

Open src/main.js and it should look like this:

import Vue from 'vue'
import App from './App.vue'
import firebase from 'firebase'
import 'firebase/firestore'
import VueFirestore from 'vue-firestore'

firebase.initializeApp({
databaseURL: "https://xxxxxxxxxx.firebaseio.com",
projectId: "xxxxxxxxxx",
});
export const db = firebase.firestore();
Vue.use(VueFirestore)

Vue.config.productionTip = false

new Vue({
    render: h => h(App),
}).$mount('#app')

Replace the value of databaseURL and projectID properties with the ones from the snippet given to you by Firebase. In this part, we initialized a Firebase project and exported the firestore database so that we can use it in the components of our application. We will use the vue-firestore plugin to easily sync our data with Firestore. We will also use lodash to get the sum of all entries.

Add Entry

We will now proceed to adding entries to our expenses list. Open src/components/HelloWorld.vue and update the code with this:

<template>
    <div>
        <div v-if="expenses.length">
            <ol>
                <li v-for="(expense, index) in expenses" :key="index">
                    {{expense.name}} - {{expense.amount}}
                </li>
            </ol>
            <p>Total: {{total}}</p>
        </div>

        <div v-else>
            <p>Hooray! You didn't spend anything.</p>
        </div>

        <hr>
        <p>New Expense:</p>
        <form v-on:submit.prevent>
            <div>
                <input type="text" v-model="name" placeholder="What did you buy?">
            </div>
            <div>
                <input type="text" v-model="amount" placeholder="How much is it?">
            </div>
            <div>
                <button @click="addExpense()">Submit</button>
            </div>
        </form>
    </div>
</template>

<script>
import {db} from '../main.js'
import _ from 'lodash'
export default {
    name: 'HelloWorld',
    data: () => ({
        name: '',
        amount: '',
        total: 0,
        expenses: []
    }),
    firestore() {
        return {
            expenses: db.collection('expenses')
        }
    },
    methods: {
        addExpense() {
            this.$firestore.expenses.add({
                name: this.name,
                amount: this.amount,
                dateCreated: new Date()
            })
            this.name = ''
            this.amount = ''
        }
    },
    watch: {
        expenses() {
            this.total = _.sumBy(this.expenses, function(expense) { return parseFloat(expense.amount); });
        }
    }
}
</script>

<style scoped>
    div {
        margin-bottom: 20px;
    }
</style>


Yes, yes I know, this is a massive change. Let’s get into it section by section.

First, we updated the template to display the list of expenses and the total amount of these expenses. We used lodash to get the sum of expenses using the amount property and assigned it to total data object property. The total was computed every time the expenses are updated. This was done using a watcher that responds to a changing data.

Second, we added a form to allow us to add an expense. We assigned name and amount data object properties to text fields as their model. Once the button is clicked, the addExpense method will be triggered and will add this new expense entry straight to the firestore collection.

Last, when the expense was successfully added to the collection, the expenses binding will be updated and the total will be recomputed. The form will also be cleared, allowing for new expense to be entered.

Delete Entry

Say you mistakenly added an expense entry for some unknown reason, there should be a way to delete it, isn’t it? So let’s add a capability to delete some record from the collection. Update the listing part to look like this:

<li v-for="(expense, index) in expenses" :key="index">
{{expense.name}} - {{expense.amount}} <button @click="removeExpense(expense)">Remove</button>
</li>

Add a new method to remove the desired expense.

removeExpense(expense){
  this.$firestore.expenses.doc(expense['.key']).delete();
}

If all goes well the way we want it to be, our Firestore database will look something like this:

Demo, demo, demo

Complete Code for HelloWorld.vue

<template>
    <div>
        <div v-if="expenses.length">
            <ol>
                <li v-for="(expense, index) in expenses" :key="index">
                    {{expense.name}} - {{expense.amount}} <button @click="removeExpense(expense)">Remove</button>
                </li>
            </ol>
            <p>Total: {{total}}</p>
        </div>

        <div v-else>
            <p>Hooray! You didn't spend anything.</p>
        </div>

        <hr>
        <p>New Expense:</p>
        <form v-on:submit.prevent>
            <div>
                <input type="text" v-model="name" placeholder="What did you buy?">
            </div>
            <div>
                <input type="text" v-model="amount" placeholder="How much is it?">
            </div>
            <div>
                <button @click="addExpense()">Submit</button>
            </div>
        </form>
    </div>
</template>

<script>
import {db} from '../main.js'
import _ from 'lodash'
export default {
    name: 'HelloWorld',
    data: () => ({
        name: '',
        amount: '',
        total: 0,
        expenses: []
    }),
    firestore() {
        return {
            expenses: db.collection('expenses')
        }
    },
    methods: {
        addExpense() {
            this.$firestore.expenses.add({
                name: this.name,
                amount: this.amount,
                dateCreated: new Date()
            })
            this.name = ''
            this.amount = ''
        },
        removeExpense(expense){
            this.$firestore.expenses.doc(expense['.key']).delete();
        }
    },
    watch: {
        expenses() {
            this.total = _.sumBy(this.expenses, function(expense) { return parseFloat(expense.amount); });
        }
    }
}
</script>

<style scoped>
    div {
        margin-bottom: 20px;
    }
</style>

Conclusion

That’s it, you now have a working fullstack web app using only Firebase and Vue.js. Let’s hope that billions of people will realize that they need to track their expenses and use our app that can scale to billion of users. Join me next time in developing fullstack applications using our tools of choice: Firebase and Vue.js. See you next time!


5 Comments

Cody Bontecou · February 9, 2019 at 8:27 am

I am getting a warning due to this line: Vue.use(VueFire)

    Jacob Fernandez · February 9, 2019 at 9:00 am

    Hi, my bad, I forgot to update the code snippet. Please check again. Thanks!

      Cody Bontecou · February 9, 2019 at 1:30 am

      Ah, nevermind. I see the new import as well. Thank you for taking the time to update this.

      Cody Bontecou · February 9, 2019 at 9:22 am

      Is the only thing you changed ‘Vue.use(VueFirestore)’? If so, I’m still running into an error.

Vue Authentication with Firebase Auth - Code Construction Ahead · March 13, 2019 at 12:06 pm

[…] In this tutorial, we will create an application with authentication using Vue and Firebase Authentication UI. You can add what you’ll learn here to my previous post titled “Fullstack with Firebase and Vue.js“ […]

Leave a Reply

%d bloggers like this: