Vue 3 Firebase example: Build a CRUD Application

In this tutorial, I will show you step by step to build a Vue 3 Firebase CRUD App example with Realtime Database.

Related Posts:
Vue 3 Firestore example: Build a CRUD Application
Vue 3 CRUD example with Axios & Vue Router
Vue 3 Authentication with JWT, Vuex, Axios and Vue Router


Vue 3 Firebase CRUD example Overview

We’re gonna build an Vue 3 Firebase example to make CRUD Operations using firebase library in which:

  • Each Tutorial has key, title, description, published status.
  • We can create, retrieve, update, delete Tutorials (CRUD operations) from Firebase Realtime Database

Here are the screenshots:

– Create a new Tutorial:

vue-3-firebase-crud-example-create

Firebase Realtime Database right after the Operation:

vue-3-firebase-crud-example-create-db

– Retrieve all Tutorials with details when clicking on a Tutorial:

vue-3-firebase-crud-example-retrieve

– Change status to Published/Pending using Publish/UnPublish button:

vue-3-firebase-crud-example-update-status

– Update the Tutorial details with Update button:

vue-3-firebase-crud-example-update

– Delete the Tutorial using Delete button:

vue-3-firebase-crud-example-delete

– Delete all Tutorials with Remove All button:

vue-3-firebase-crud-example-delete-all

CRUD Operations using firebase Reference

We’re gonna use instance of firebase.database.Reference to read/write data from the Firebase database.

var tutorialsRef = firebase.database().ref("/tutorials");

– Read list once using once():

tutorialsRef.once('value', function(snapshot) {
  vat tutorials = [];

  snapshot.forEach(function(childSnapshot) {
    var key = childSnapshot.key;
    var data = childSnapshot.val();
    // ...

    tutorials.push({ key: key, title: data.title, description: data.description});
  });
});

– Read List with listening to the data changes using on():

tutorialsRef.on('child_added', function(data) {
  // data.key, data.val().title, data.val().description
});

tutorialsRef.on('child_changed', function(data) {
  // data.key, data.val().title, data.val().description
});

tutorialsRef.on('child_removed', function(data) {
  // data.key, data.val().title, data.val().description
});

– Listening for all value events on a List reference

var onDataChange =tutorialsRef.on('value', function(snapshot) {
  snapshot.forEach(function(childSnapshot) {
    var childKey = childSnapshot.key;
    var childData = childSnapshot.val();
    // ...
  });
});

– Remove the listener using off():

tutorialsRef.off("value", onDataChange);

– Create a new object in List:

tutorialsRef.push({
  title: "bezkoder Tut#1",
  description: "Helpful tutorial"
});

– Update object in List:
+ destructive update using set(): delete everything currently in place, then save the new value

tutorialsRef.child(key).set({
  title: 'zkoder Tut#1',
  description: 'Tut#1 Description'
});

+ non-destructive update using update(): only updates the specified values

tutorialsRef.child(key).update({
  title: 'zkoder new Tut#1'
});

– Delete an object in List:

tutorialsRef.child(key).remove();

– Delete entire List:

tutorialsRef.remove();

Technology

  • Vue 3
  • Vue Router 4
  • Firebase 8
  • Bootstrap 4

Setup the Firebase Project

Go to Firebase Console, login with your Google Account, then click on Add Project.

You will see the window like this:

vue-3-firebase-crud-example-create-project

Enter Project name, set Project Id and click on Continue.
Turn off Enable Google Analytics for this project, then click Create Project.

Now, browser turns into following view:

vue-3-firebase-crud-example-web-app

If you don’t see it, just choose Project Overview.
Click on Web App, you will see:

vue-3-firebase-crud-example-register-app

Set the nickname and choose Register App for next step.

vue-3-firebase-crud-example-add-firebase-sdk

Copy the script for later use.

Choose Database in the left (list of Firebase features) -> Realtime Database -> Create Database.

vue-3-firebase-crud-example-create-database

In this tutorial, we don’t implement Authentication, so let’s choose test mode:

vue-3-firebase-crud-example-config-rules

Or if you come from another situation, just open Tab Rules, then change .read and .write values to true.

Setup Vue 3 Firebase Project

Open cmd at the folder you want to save Project folder, run command:
vue create vue-3-firebase

You will see some options, choose Default ([Vue 3] babel, eslint).
After the process is done. We create new folders and files like the following tree:


public

index.html

src

components

AddTutorial.vue

Tutorial.vue

TutorialsList.vue

services

TutorialDataService.js

App.vue

firebase.js

main.js

router.js

package.json


Let me explain it briefly.

package.json contains 3 main modules: vue, vue-router, firebase.
firebase.js configures information to connect with Firebase Project and export Firebase Database service.
TutorialDataService exports TutorialDataService that uses firebase‘s Database Reference to interact with Firebase Database.
– There are 3 components that uses TutorialDataService:

  • AddTutorial for creating new item
  • TutorialsList contains list of items, parent of Tutorial
  • Tutorial shows item details

router.js defines routes for components.
App.Vue contains Router View and navigation bar.

Add Bootstrap to Vue 3 Firebase example

Run command: npm install [email protected] jquery popper.js.

Open src/main.js and import Bootstrap as following-

import { createApp } from 'vue'
import App from './App.vue'
import 'bootstrap'
import 'bootstrap/dist/css/bootstrap.min.css'
...

Add Vue Router to Vue 3 Firebase example

– Run the command: npm install [email protected].

– In src folder, open router.js and define Router as following code:

import { createWebHistory, createRouter } from "vue-router";

const routes =  [
  {
    path: "/",
    alias: "/tutorials",
    name: "tutorials",
    component: () => import("./components/TutorialsList")
  },
  {
    path: "/add",
    name: "add",
    component: () => import("./components/AddTutorial")
  }
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

We create the routes as an array, each route has:

  • path: the URL path where this route can be found.
  • name: optional name to use when we link to this route.
  • component: component to load when this route is called.

We also use createWebHistory to switch from using hash to history mode inside the browser, using the HTML5 history API.

– Open src/main.js and import the router in our application:

...
import router from './router'

createApp(App).use(router).mount('#app')

Add Navbar and Router View to Vue 3 Firebase example

Let’s open src/App.vue, this App component is the root container for our application, it will contain a navbar.

<template>
  <div id="app">
    <nav class="navbar navbar-expand navbar-dark bg-dark">
      <router-link to="/" class="navbar-brand">bezKoder</router-link>
      <div class="navbar-nav mr-auto">
        <li class="nav-item">
          <router-link to="/tutorials" class="nav-link">Tutorials</router-link>
        </li>
        <li class="nav-item">
          <router-link to="/add" class="nav-link">Add</router-link>
        </li>
      </div>
    </nav>

    <div class="container mt-3">
      <h2>Vue 3 Firebase CRUD example</h2>
      <router-view />
    </div>
  </div>
</template>

<script>
export default {
  name: "app"
};
</script>

<style scoped>
.container h2 {
  text-align: center;
  margin: 25px auto;
}
</style>

Integrate Firebase into Vue 3 App

First run the command: npm install [email protected].

Open src/firebase.js, import firebase library and add configuration that we have saved when Popup window was shown:

import firebase from "firebase/app";
import "firebase/database";

let config = {
  apiKey: "xxx",
  authDomain: "bezkoder-firebase.firebaseapp.com",
  databaseURL: "https://bezkoder-firebase.firebaseio.com",
  projectId: "bezkoder-firebase",
  storageBucket: "bezkoder-firebase.appspot.com",
  messagingSenderId: "xxx",
  appId: "xxx",
};

firebase.initializeApp(config);

export default firebase.database();

Don’t forget to export firebase.database.Database service with firebase.database().

Create Data Service

This service will use Firebase Database service to interact with Firebase Realtime Database. It contains necessary functions for CRUD operations.

services/TutorialDataService.js

import firebase from "../firebase";

const db = firebase.ref("/tutorials");

class TutorialDataService {
  getAll() {
    return db;
  }

  create(tutorial) {
    return db.push(tutorial);
  }

  update(key, value) {
    return db.child(key).update(value);
  }

  delete(key) {
    return db.child(key).remove();
  }

  deleteAll() {
    return db.remove();
  }
}

export default new TutorialDataService();

Component for creating Object

This component has a Form to submit new Tutorial with 3 fields: title, description & published (false by default). It calls TutorialDataService.create() method.

vue-3-firebase-crud-example-create

components/AddTutorial.vue

<template>
  ...
</template>

<script>
import TutorialDataService from "../services/TutorialDataService";

export default {
  name: "add-tutorial",
  data() {
    return {
      tutorial: {
        title: "",
        description: "",
        published: false
      },
      submitted: false
    };
  },
  methods: {
    saveTutorial() {
      var data = {
        title: this.tutorial.title,
        description: this.tutorial.description,
        published: false
      };

      TutorialDataService.create(data)
        .then(() => {
          console.log("Created new item successfully!");
          this.submitted = true;
        })
        .catch(e => {
          console.log(e);
        });
    },
    
    newTutorial() {
      this.submitted = false;
      this.tutorial = {
        title: "",
        description: "",
        published: false
      };
    }
  }
};
</script>

There are 2 main variables return from data():
tutorial for tutorial data
submitted status

We also have a function to get value of the form (state) and call TutorialDataService.create() method.

For the template, we check the submitted state, if it is true, we show Add button for creating new Tutorial again. Otherwise, a Form will display.

<template>
  <div class="submit-form">
    <div v-if="!submitted">
      <div class="form-group">
        <label for="title">Title</label>
        <input
          type="text"
          class="form-control"
          id="title"
          required
          v-model="tutorial.title"
          name="title"
        />
      </div>

      <div class="form-group">
        <label for="description">Description</label>
        <input
          class="form-control"
          id="description"
          required
          v-model="tutorial.description"
          name="description"
        />
      </div>

      <button @click="saveTutorial" class="btn btn-success">Submit</button>
    </div>

    <div v-else>
      <h4>You submitted successfully!</h4>
      <button class="btn btn-success" @click="newTutorial">Add</button>
    </div>
  </div>
</template>

<script>
import TutorialDataService from "../services/TutorialDataService";

export default {
  name: "add-tutorial",
  ...
};
</script>

Component for List of Objects

This component has:

  • a tutorials array displayed as a list on the left.
  • a selected Tutorial which is shown on the right.

vue-3-firebase-crud-example-retrieve

So we will have following state:

  • tutorials
  • currentTutorial and currentIndex

We also need to use 2 TutorialDataService methods:

  • getAll()
  • deleteAll()

And add Tutorial into this component as child component.

components/TutorialsList.Vue

<template>
  ...
</template>

<script>
import TutorialDataService from "../services/TutorialDataService";
import TutorialDetails from "./Tutorial";

export default {
  name: "tutorials-list",
  components: { TutorialDetails },
  data() {
    return {
      tutorials: [],
      currentTutorial: null,
      currentIndex: -1
    };
  },
  methods: {
    onDataChange(items) {
      let _tutorials = [];

      items.forEach((item) => {
        let key = item.key;
        let data = item.val();
        _tutorials.push({
          key: key,
          title: data.title,
          description: data.description,
          published: data.published,
        });
      });

      this.tutorials = _tutorials;
    },

    refreshList() {
      this.currentTutorial = null;
      this.currentIndex = -1;
    },

    setActiveTutorial(tutorial, index) {
      this.currentTutorial = tutorial;
      this.currentIndex = index;
    },

    removeAllTutorials() {
      TutorialDataService.deleteAll()
        .then(() => {
          this.refreshList();
        })
        .catch((e) => {
          console.log(e);
        });
    },
  },
  mounted() {
    TutorialDataService.getAll().on("value", this.onDataChange);
  },
  beforeDestroy() {
    TutorialDataService.getAll().off("value", this.onDataChange);
  }
};
</script>

In the code above, we add a listener for data value changes in mounted() and detach the listener in beforeDestroy().

Inside listener function, we get the key and other fields of each item. This key is unique and important for update operation.

We also have refreshList() function for every time delete operation is done.

Let’s continue to implement the template:

<template>
  <div class="list row">
    <div class="col-md-6">
      <h4>Tutorials List</h4>
      <ul class="list-group">
        <li
          class="list-group-item"
          :class="{ active: index == currentIndex }"
          v-for="(tutorial, index) in tutorials"
          :key="index"
          @click="setActiveTutorial(tutorial, index)"
        >
          {{ tutorial.title }}
        </li>
      </ul>

      <button class="m-3 btn btn-sm btn-danger" @click="removeAllTutorials">
        Remove All
      </button>
    </div>
    <div class="col-md-6">
      <div v-if="currentTutorial">
        <tutorial-details
          :tutorial="currentTutorial"
          @refreshList="refreshList"
        />
      </div>
      <div v-else>
        <br />
        <p>Please click on a Tutorial...</p>
      </div>
    </div>
  </div>
</template>

<script>...
import TutorialDetails from "./Tutorial";

export default {
  name: "tutorials-list",
  components: { TutorialDetails },
  ...
};
</script>

<style>
.list {
  text-align: left;
  max-width: 750px;
  margin: auto;
}
</style>

You can see that when we click on any item, setActiveTutorial() function will be invoked to change current active Tutorial, which data is passed to Tutorial component.

Component for Object details

This component is the child of TutorialsList component. It bind tutorial data and invoke refreshList of the parent.

For getting update, delete the Tutorial, we’re gonna use two TutorialDataService methods:

  • update()
  • delete()

components/Tutorial.vue

<template>
  ...
</template>

<script>
import TutorialDataService from "../services/TutorialDataService";

export default {
  name: "tutorial",
  props: ["tutorial"],
  data() {
    return {
      currentTutorial: null,
      message: "",
    };
  },
  watch: {
    tutorial: function(tutorial) {
      this.currentTutorial = { ...tutorial };
      this.message = "";
    },
  },
  methods: {
    updatePublished(status) {
      TutorialDataService.update(this.currentTutorial.key, {
        published: status,
      })
        .then(() => {
          this.currentTutorial.published = status;
          this.message = "The status was updated successfully!";
        })
        .catch((e) => {
          console.log(e);
        });
    },

    updateTutorial() {
      const data = {
        title: this.currentTutorial.title,
        description: this.currentTutorial.description,
      };

      TutorialDataService.update(this.currentTutorial.key, data)
        .then(() => {
          this.message = "The tutorial was updated successfully!";
        })
        .catch((e) => {
          console.log(e);
        });
    },

    deleteTutorial() {
      TutorialDataService.delete(this.currentTutorial.key)
        .then(() => {
          this.$emit("refreshList");
        })
        .catch((e) => {
          console.log(e);
        });
    },
  },
  mounted() {
    this.message = "";
    this.currentTutorial = { ...this.tutorial }
  },
};
</script>

And this is the code for template:

<template>
  <div v-if="currentTutorial" class="edit-form">
    <h4>Tutorial</h4>
    <form>
      <div class="form-group">
        <label for="title">Title</label>
        <input
          type="text"
          class="form-control"
          id="title"
          v-model="currentTutorial.title"
        />
      </div>

      <div class="form-group">
        <label for="description">Description</label>
        <input
          type="text"
          class="form-control"
          id="description"
          v-model="currentTutorial.description"
        />
      </div>

      <div class="form-group">
        <label><strong>Status:</strong></label>
        {{ currentTutorial.published ? "Published" : "Pending" }}
      </div>
    </form>

    <button
      class="badge badge-primary mr-2"
      v-if="currentTutorial.published"
      @click="updatePublished(false)"
    >
      UnPublish
    </button>
    <button
      v-else
      class="badge badge-primary mr-2"
      @click="updatePublished(true)"
    >
      Publish
    </button>

    <button class="badge badge-danger mr-2" @click="deleteTutorial">
      Delete
    </button>

    <button type="submit" class="badge badge-success" @click="updateTutorial">
      Update
    </button>
    <p>{{ message }}</p>
  </div>

  <div v-else>
    <br />
    <p>Please click on a Tutorial...</p>
  </div>
</template>

<script>...
export default {
  name: "tutorial",
  props: ["tutorial"],
  ...
};
</script>

<style>
.edit-form {
  max-width: 300px;
  margin: auto;
}
</style>

Run & Check

You can run this App with command: npm run serve.

 DONE  Compiled successfully!

  App running at:
  - Local:   http://localhost:8080/
  - Network: http://192.168.1.7:8080/

  Note that the development build is not optimized.
  To create a production build, run npm run build.

Open browser with url: http://localhost:8080/ and check the result.

Conclusion

Today we’ve built Vue 3 Firebase example with a CRUD Application successfully using firebase library to interact with Realtime Database. Now we can display, modify, delete object and list at ease.

If you want to use Firestore instead:
Vue 3 Firestore example: Build a CRUD Application

You can also find how to create Vue HTTP Client for working with Restful API in:
Vue 3 CRUD example with Axios & Vue Router

Happy learning, see you again!

Further Reading

Fullstack CRUD App:

Source Code

You can find the complete source code for this tutorial on Github.

3 thoughts to “Vue 3 Firebase example: Build a CRUD Application”

Leave a Reply

Your email address will not be published. Required fields are marked *