Firebase CRUD operations with JavaScript and jQuery

Learn how to do Read and Write Data operations on Firebase Realtime Database i.e. Firebase CRUD operations with JavaScript and jQuery.

Firebase CRUD operations with JavaScript and jQuery

In this tutorial, we will learn how to do data operations on Firebase, i.e., CRUD operations in Firebase with JavaScript and jQuery.

This tutorial is a follow-up to the tutorial Auth Schemes in Google Firebase and Custom Router for Firebase jQuery App.

To understand Firebase CRUD operations, we will have the Movie as our entity which is being added, viewed, updated, or removed.


Router

As our custom router is going to load the template and fill it with data in the following way:

const Auth = require('./auth');
const Router = require('./router');

//Redirect to some Page or URL
const redirect = function(to) {
  appRouter.go(to)
}

const appRouter = new Router({
  mountPoint: '#root',
  indexRoute: 'index',
  routes: {
    login : {
      path: 'login',
      templateUrl: 'partials/login.html',
      onEnter: function() {
        var user = Auth.checkLoggedInUser();
        if( !user && window.location.hash.match('/login') ){
          return true;
        } else {
          return 'index';
        }
      },
      controller: require('./controllers/login')(Auth, redirect)
    }
  }
});

Here in the above code, you can see the controller key for a particular route. Which grabs the module and executes it with its dependencies i.e. Auth and redirect.


Add and Update

Add Data

So To Add the data, the following method from Firebase API can be used:

const key = firebase.database().ref().child('movies').push(data).key;

In the above code, we are getting the reference of DB from firebase.database().ref(). And then going to the movies node by .child(‘movies’) where we are going to save the new data.

And then pushing it to the node by .push(data). This saves the data to Firebase and returns the newly created object reference.

So the .key attribute after push will give the key with which new data has been saved.

We want to save the same data in two different locations/endpoints simultaneously. So we will follow the Update approach to add the new data.

At first, we push the empty data to generate a new empty node with a key. And then do update operations on the key itself. Here's the code to demonstrate that:

const Key = firebase.database().ref().child('movies').push().key;

// Write the new post's data simultaneously in the movies list and the user's movie list.
const updates = {};
updates['/movies/' + Key] = movie;
updates['/user-movies/' + uid + '/' + Key] = movie;

return firebase.database().ref().update(updates);

Here we have created an associative array with all the updates we want to perform. So for two paths, the same data is going to be saved.


Update Data

This updates array can be passed to the update method on the Firebase DB reference as firebase.database().ref().update(updates).

In our example, it will be done in three steps

  1. Load the Data
  2. Fill The Data
  3. After edits, on clicking save, Save the data on the same reference

The first and third steps will utilize the key they receive from the router. The following code demonstrates that:

//See complete file at 
//https://github.com/time2hack/movie-db/blob/master/src/js/controllers/edit.js
const query = firebase.database().ref("movies/"+params.id);

//Fire Query
query.once("value").then(fillData)

//Fill The form data
function fillData(snap) {
  const data = snap.val();
  console.log(data)
  $('#movieName').val(data.movieName);
  $('#releaseYear').val(data.releaseYear);
  $('#generes').val((data.generes || []).join(', '))
  $('#duration').val(data.duration);
  $('#directors').val((data.directors || []).join(', '))
  $('#actors').val((data.actors || []).join(', '))
  $('#imdbUrl').val(data.imdbUrl);
}

//Save function (similar to Adding the data. 
//This time we don't generate new key but utilize the one received from Router parameter
function saveMovie(movie) {
  const uid = firebase.auth().currentUser.uid;
  const postKey = params.id; //Params is received from the Router
  const updates = {};
  updates['/movies/' + postKey] = movie;
  updates['/user-movies/' + uid + '/' + postKey] = movie;

  return database.ref().update(updates);
}

Read

To list and view the data, we need to execute the query. And the Firebase DB reference is a query by itself. So creating the reference and attaching the event handler will let us read and render the results.

Following code queries and passes to the render function for a single item.

const query = firebase.database().ref("movies").limitToFirst(20);
query.once("value")
  .then(function(snapshot) {
    snapshot.forEach(renderSingleSnapshot);
  }).then(function(){
    $(document).find('#list').html(markup);
  });

const renderSingleSnapshot = function(singleSnapshot){
  // ... code to render
}

The data received from Firebase is called snapshot. The snapshot has few methods and iterators with it. Those can retrieve values and keys from snapshots or loop over the data.

const firebase = require('firebase');
const $ = require('jquery');
const durationConvertor = require('../utils/duration');

const ListController = function() {
  return function () {
    const userId = firebase.auth().currentUser.uid;

    // Get a reference to the database service
    let markup = '';
    const database = firebase.database();
    const query = database
      .ref("movies")
      .orderByChild('createdAt')
      .limitToLast(20);

    query.once("value")
      .then(function(snapshot) {
        snapshot.forEach(renderSingleSnapshot);
      }).then(function(){
        $(document).find('#list').html(markup);
      });

    const renderSingleSnapshot = function(movieRef) {
      const movie = movieRef.val();
      console.log(movieRef.key, movie);

      let imdb = '';
      let editLink = '';
      let html = '';
      if(movie.uid === userId) {
        editLink = ` <a href="#/edit/${movieRef.key}+"><i class="fa fa-pencil" aria-hidden="true"></i></a>`;
      }
      viewLink = `<a href="#/view/${movieRef.key}">${movie.movieName}</a>`
      if( movie.imdbUrl !== '' ){
        imdb += ` <a href="${movie.imdbUrl}" target="_blank"><i class="fa fa-imdb" aria-hidden="true"></i></a>`;
      }

      html += `
        <li class="list-group-item media movie">
          <div class="media-body">
            <h5 class="media-heading">${viewLink}${imdb}${editLink}</h5>
            <h6><b>Director: </b>${movie.directors.join(', ')}</h6>
            <small><b>Released in: </b>${(movie.releaseYear)}<br/>
            <b>Duration: </b>${durationConvertor(movie.duration)}<br/>
            <b>Actors: </b>
            <small>${(movie.actors || movie.stars).join(', ')}</small>
          </div>
          <div class="media-right">
            <img class="media-object" height="125" src="${movie.poster}" alt="${movie.movieName}" />
          </div>
        </li>`;

      //Add new ones on top
      markup = html + markup;
    }
  }
}

ListController.toggleStar = function(movieUniqueId){
  console.log(movieUniqueId);
}

module.exports = ListController;

Remove

Removing the data is also not a hassle, though we are not implementing it in our MovieDB application.

The following code shows that remove() the method can be used to remove the key reference:

firebase.database().ref("movies").child(key).remove();

So creating a function and calling it with an event handler will look like that. Though the button to trigger the delete event must have the data-key attribute. And this attribute's value is the key that needs to be removed.

const remove = (e) => {
  e.preventDefault();
  e.stopPropogation();
  var key = $(this).data('key');
  if(confirm('Are you sure?')){
    firebase.database().ref("movies").child(key).remove();
  }
}
$(document).on('click', '.btn-remove-key', remove);

Hence the above code will be functional for the buttons and links created like this:

<button type="button" class="btn-remove-key" data-key="-dsdsf-sample-key">Delete</button>
<a ref="#" class="btn-remove-key" data-key="-dsdsf-sample-key">Delete</a>
<span class="btn-remove-key" data-key="-dsdsf-sample-key">Delete</span>

And this is how we can perform Firebase CRUD Operations.


Conclusion

Let me know through comments. or on Twitter at @heypankaj_ and/or @time2hack

If you find this article helpful, please share it with others.

Subscribe to the blog to receive new posts right to your inbox.