firebaseThe Realtime Database API in Firebase is based on callbacks. This could be fine for you if you are maintaining a codebase which is a few years old.

But, if you prefer a Promise, or better yet async await approach, you will be down for a little disappointment.

A typical function that you write for retrieving data from a particular path in the Realtime database will look like as follows:

const admin = require("firebase-database");
const realtimeDb = admin.database();
const ref = realtimeDb.ref(`/path/to/the/data`);

/** Called on `value` event */
const onDataCallback = (data) => {
  // Do whatever you want with the data
  console.log("data:", data);
};

ref.on("value", onDataCallback);

We are dealing with callbacks here, so we cannot use then(), or await for our async operations.

Let us start with an example.

Let’s suppose, you want to fetch data from the path:

/Users/usernames/hopelesspuppy

For that, you would first need to create a reference to this path.

const ref = admin.database().ref("/Users/usernames/hopelesspuppy");

Suppose you want to fetch some data from a certain path from the Realtime Database. To fetch the data using the ref, you would write your code as follows:

const getData = ref => {
  return Promise((resolve, reject) => {
    const onData = data => resolve(data));
    const onError = error => reject(error));

    ref.on('value', onData, onError);
  });
};

If you have read the docs, you’d know how this code works.

But, for the uninitiated, let me reiterate.

ref.on("value", onData, onError);

The ref denotes the reference to the item in the database which you want to perform an action like read or write.

When you set a value event on the item reference in the Realtime database, one of the callbacks you provide will be called.

In this instance, I have named my callbacks onData and onError.

As the name suggests, if the retrieval is successful, the onData function will execute with the snapShot of the object. You can access its value using the .val() method on that snapshot.

In case of an error, your the onError function will execute where I’m only printing the error as a string.

const onData = (snapShot) => {
  console.log(snapShot.val());
};

const onError = (error) => {
  console.log("Error retrieving the data:", error.toString());
};

So, how do we convert this callback-based code into a Promise.

The answer is simple. Use the Promise constructor.

Or to be more precise, you just wrap your callback code into a Promise constructor. This constructor resolves or rejects based on onData or onError callback execution.

Here’s the code:

const getData = (ref) => {
  return new Promise((resolve, reject) => {
    const onError = error => reject(error);
    const onData = snap => resolve(snap.val());

    ref.on("value", onData, onError);
  });
};

getData(ref)
  .then((value) => {
    // resolve() was called
  })
  .catch((error) => {
    // reject() was called
    // Something went wrong while fetching the data.
    // Handle that error here.
  });

That is all.

2 Responses

  1. hello! i’ve been trying to get this to work but const onError says error is not defined.

    i commented out that line but it returns null instead of a promise.

    if it helps, i’m trying to use material-table and populate with a call to a firebase realtime database.

    thanks!

    1. “`const getData = (ref) => {
      return new Promise((resolve, reject) => {
      const onError = (error = reject(error)); // this is a typo.
      const onData = (snap) => resolve(snap.val());

      ref.on("value", onData, onError);
      

      });
      };“`

      This has a typo. I fixed it in the post.

      const onError = (error) => reject(error);

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.