Full Stack Vue.js with Firestore

Ten years ago, the idea of building a realtime web app was simply out of reach for the average developer. Accomplishing such a feat would require some impressive jujutsu to connect a pub/sub server to a database to websockets and back again.

Fast forward to the present and meet Firebase: realtime cloud infrastructure for client-side apps. Firebase can turn any Frontend tool (Vue, Angular, iOS, etc.) into a full-stack product capable of scaling infinitely in the cloud. It abstracts away most of your complex server-side features like user authentication, data persistence, file storage, and microservices, so you can focus on building an awesome experience for the end user.

This tutorial will take you from zero to a realtime Vue app with just a few lines of code. We’re going to build a simple web app that can create, read, and delete data about locations around the world.

The app we’re building

Meet the Firestore NoSQL Database

In late 2017, Firebase launched a new NoSQL Document Database called Cloud Firestore. What makes Firestore stand out among its peers (MongoDB, AWS DynamoDB) is that it syncs data across all client apps with realtime listeners. This doesn’t just make realtime features easier to build — it makes them automatic.

Firestore stores JSON-like data into documents, then organizes them into collections that can be queried. Additionally, Firestore gives you a powerful query language for modeling complex data relationships, but that’s beyond the scope of this tutorial

Let’s look at an example. As a human, you might say something like Give me all documents in a collection where the city equals Orlando, which is expressed in Firestore code as locations.where('city', '==', 'Orlando').

Getting Started

Fork the project and experiment

Let’s kick things off from the command line. This tutorial assumes you have Node.js and NPM installed on your local system. We can then install vue-cli to rapidly prototype our app from the command line.

npm install -g vue-cli

Now let’s generate and serve a basic Vue application using the CLI.

vue init webpack fire-app
cd fire-app
npm run dev

In the browser, we should see the basic template being served at http://localhost:8080. Let’s open our IDE so we can start writing some code.

Installing Firebase and VueFire

At this point, we have a blank Vue template that is ready for some magic from the cloud. Let’s open a Firebase account and start a new project on the free Spark pricing tier.

From inside your Firesbase project, click on Add Firebase to your Web App and make a note of the databaseURL and projectID.

Our next step is to install Vuefire, which provides bindings for Firebase in a Vue project. The main Firebase Web SDK returns a snapshot object when requesting a document, but that’s not necessary for most implementations. VueFire steps in and formats the snapshot as a plain JavaScript object to make your life even easier.

Cloud Firestore is a cutting-edge tool and is available in public beta as of January 2018. That being said, we need to install Vuefire with the @next tag to take advantage of it. Run the following command inside your project:

npm install vuefire@next firebase --save

The final step is to initialize Firestore with our Firebase project credentials. Open the main.js file in your IDE and add the following lines of code:

import VueFire from 'vuefire'
import firebase from 'firebase/app'
import 'firebase/firestore'
Vue.use(VueFire)
firebase.initializeApp({
projectId: 'YOUR_PROJECT_ID',
databaseURL: 'YOUR_DB_URL'
})
export const db = firebase.firestore()

So what did we do here?

  1. Told Vue to use the VueFire plugin with Vue.use(VueFire)
  2. Initialized Firebase with our project using firebase.initializeApp
  3. Exported the database as a variable named db for use in other components

Managing Data in the Firestore Console

Before writing any more code, let’s get acquainted with the Firestore database console. The console provides an intuitive way for the admin (that’s you) to visualize and manage data. Navigate to Database → Cloud Firestore → Try Firestore Beta.

On the initial setup, you will be asked about security rules. For now, choose Test Mode to bypass all security.

From here, we can create a new collection called locations.

Our first document will be created inside this collection. Firestore will automatically generate an ID for the document, so your job is to give it a name, image, and createdAt timestamp.

That was easy, but how do we consume this data from Vue?

Reading Firestore Documents from Vue

Now that we have a document in Firestore, let’s start by reading it from a Vue component. The Vue CLI already generated a HelloWorld.vue component, so we can write all of our code in there.

Jump down do the <script> tag and update the following line:

import { db } from '../main'
export default {
name: 'HelloWorld',
data () {
return {
locations: []
}
},
firestore () {
return {
locations: db.collection('locations').orderBy('createdAt')
}
}
}

The data () method models the initial properties used by the component. Our locations will start out as an empty array.

The firestore () method will make the query to the database to populate this array with our actual persistent data. Also notice the extra orderBy('createdAt') method we chained on the query. It tells Firestore to return the documents ordered from oldest to newest.

Now jump up to the <template> tag and loop over the locations with v-for.

<div>
<article v-for="(location, idx) in locations" :key="idx">
<img :src="location.image">
<h1>{{ location.name }}</h1>
</article>
</div>

Every location in this list is just a JavaScript object, so we can do cool things like bind a url to an image’s :src attribute.

Also, keep in mind that this list is realtime. Try it out by adding another document to the collection in the Firestore console and watch it render in your app automagically.

Creating New Documents

So far, we have been creating data from the Firebase admin console, but what we really want is for our users to create their own data.

Let’s make a few updates to the component script. Initialize properties for a new location’s name and image, then add a methods object.

export default {
name: 'HelloWorld',
data () {
return {
locations: [],
name: '', // <-- here
image: '' // <-- here
}
},
firestore () {
return {
locations: db.collection('locations').orderBy('createdAt')
}
},
methods: {
addLocation (name, image) { // <-- and here
const createdAt = new Date()
db.collection('locations').add({ name, image, createdAt })
}
}
}

The addLocation method takes the name and image as arguments then generates the createdAt property by instantiating a JavaScript Date object. In Firestore, we can add a new document to a collection by chaining the add(data) method and passing it a plain JavaScript object.

We can now use this method by binding it to a form with v-on:submit. Each input in the form will be bound to a v-model, allowing us to pass them as arguments through the template.

<form @submit="addLocation(name, image)">
<input v-model="name" placeholder="Location Name">
<input v-model="image" placeholder="Location Image URL">
<button type="submit">Add New Location</button>
</form>

Enter some values into the form, click submit, and your list should update immediately. Firestore does something called latency compensation to make your app respond at light speed, but that’s a topic for another day.

Deleting Documents

Now that you know how to create documents, deleting them is a piece of cake. Just add another method to the script that takes a document ID as its argument. You can point to a specific document with doc(id) then chain delete() to remove it from the database.

methods: {
// ...omitted
deleteLocation (id) {
db.collection('locations').doc(id).delete()
}
}

Now bind this method to a button click on each document within the v-for loop.

<article v-for="(location, idx) in locations" :key="idx">
<!-- omitted -->
<button @click="deleteLocation(location.id)">
Delete
</button>
</article>

Click the button and the document should disappear from your list and the database console simultaneously.

Final Thoughts

Congrats! You now have a full-stack realtime Vue app that can scale to millions of users. We’ve only just scratched the surface here, but hopefully this gives you an idea of the massive potential of the Vue-Firebase stack.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.