3 Things you can do right now to improve your Ionic and Firebase app performance

First Published: 9 September 2017
Updated on: 24 September 2018

You want to give your users the best experience when using your app, heck, and I was constantly Googling or going into the Ionic forums to ask “Why is Ionic + Firebase so slow?”.

The truth is, it isn’t slow, I was just doing a few things wrong, mostly due to a lack of JavaScript knowledge, but there were a few specific things that I could improve over time.

Today you’ll learn 3 things you can do right now inside your app to improve the speed/performance.

Keep your data flat and duplicated

Reading > Writing

It takes a while to interiorize that, but it’s true, your app is going to be reading data from the database way more than writing it, that’s why in NoSQL databases we forget about normalization, and we instead optimize our data for reading.

For example, let’s say you have this two nodes:

{
  "users": {
    "user1": {
      "name": "Jorge",
      "lastName": "Vergara",
      "age": 31
    },
    "user2": {
      "name": "Andres",
      "lastName": "Ebratt",
      "age": 30
    }
  },
  "events": {
    "event1": {
      "eventName": "Birthday party",
      "eventDate": "27-09-2017",
      "guestList": {
        "user1": true,
        "user2": true
      }
    }
  }
}

That’s the kind of data structure you’d create if you were using an SQL database.

Every time you open an event to see the guest list, you’ll need to do extra queries to the database to match the user1 id from the events node to the user1 id from the users node.

Instead, to make it easier on our app, we can set up something like this:

{
  "users": {
    "user1": {
      "name": "Jorge",
      "lastName": "Vergara",
      "age": 31,
      "events": {
        "event1": {
          "eventName": "Birthday party",
          "eventDate": "27-09-2017"
        }
      }
    },
    "user2": {
      "name": "Andres",
      "lastName": "Ebratt",
      "age": 30,
      "events": {
        "event1": {
          "eventName": "Birthday party",
          "eventDate": "27-09-2017"
        }
      }
    }
  },
  "events": {
    "event1": {
      "eventName": "Birthday party",
      "eventDate": "27-09-2017",
      "guestList": {
        "user1": {
          "name": "Jorge",
          "lastName": "Vergara"
        },
        "user2": {
          "name": "Andres",
          "lastName": "Ebratt"
        }
      }
    }
  }
}

Now, whenever you want to fetch a user, you don’t need to do any extra querying to see which events the user attended, same goes for an event’s guest list.

If you’re coming from an SQL background (MySQL, PostgreSQL, etc.) you might think this is just plain wrong, but in NoSQL, data duplication isn’t a bad thing, in fact, it’s a best practice!

Use indexOn()

When querying data from the database, Firebase allows you to do ad-hoc queries on your data using an arbitrary child key. As your app grows, the performance of this queries degrades.

If you know in advance what your indexes will be, you can define them via the .indexOn rule in your Firebase Realtime Database Rules to improve query performance.

For example:

In the data we have above, let’s say you know in advance that one of the features would be sorting users by age since you’re going to create age groups for users.

{
  "users": {
    "user1": {
      "name": "Jorge",
      "lastName": "Vergara",
      "age": 31
    }
  }
}

You can go to your Firebase console, open the database and go to the RULES tab, and there you can specify that you’ll be querying by age:

{
  "rules": {
    ".read": true,
    ".write": "auth != null",
    "users": {
      ".indexOn": "age"
    }
  }
}

Using that .indexOn rule, Firebase will index the age at the servers, improving the performance of your query.

Send code to the server

Client heavy apps have one BIG problem, you can’t control how they run on every device out there, so it might run smoothly on your iPhone 7, but it might be laggy as hell in a cheaper device.

Firebase has an excellent service called Cloud Functions, where they’ll fun your functions on their servers, allowing you to offload work from the client and moving it into the servers, that way you can offer a consistent experience independently of the device.

For example:

When you create a new user, right after creating it, you also need to store the user’s information in the database, we usually do this:

firebase
  .auth()
  .createUserWithEmailAndPassword(email, password)
  .then(newUser => {
    firebase
      .database()
      .ref(`/userProfile/${newUser.uid}`)
      .set({
        email: email
      });
  });

That code creates the new user, and after the user is created, it makes another call to the database to create a userProfile node and stores the user’s email.

If my dumb phone has no memory, it might kill your app right after you send the credentials to create the new user, which means, you end up with a user without a profile.

Instead, we can use Cloud Functions to ensure this profile node is created, and our app would only do this:

firebase.auth().createUserWithEmailAndPassword(email, password);

And then, inside our Cloud Functions, we can set up an onCreate() listener, it listens for new users and runs a function whenever it detects one:

const functions = require('firebase-functions');

const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.createProfile = functions.auth.user().onCreate(event => {
  return admin
    .database()
    .ref(`/userProfile/${event.data.uid}`)
    .set({
      email: event.data.email
    });
});

Since all that code is running on Firebase servers, all your app needs to do is send the email and password to create the user, then it can sit back, relax, and let Firebase handle the rest.

This last example is better explained here.

Next Steps

If you have any questions don’t forget you can always leave a comment below.

If you want to take a deeper dive on Ionic + Firestore you should go through my FREE course: Build your first Firestore powered Ionic app.