Work with Lists of Data on the Web

Get a database reference

To read or write data from the database, you need an instance of firebase.database.Reference:

Web

import { getDatabase } from "firebase/database";

const database = getDatabase();

Web

var database = firebase.database();

Reading and writing lists

Append to a list of data

Use the push() method to append data to a list in multiuser applications. The push() method generates a unique key every time a new child is added to the specified Firebase reference. By using these auto-generated keys for each new element in the list, several clients can add children to the same location at the same time without write conflicts. The unique key generated by push() is based on a timestamp, so list items are automatically ordered chronologically.

You can use the reference to the new data returned by the push() method to get the value of the child's auto-generated key or set data for the child. The .key property of a push() reference contains the auto-generated key.

You can use these auto-generated keys to simplify flattening your data structure. For more information, see the data fan-out example.

For example, push() could be used to add a new post to a list of posts in a social application:

Web

import { getDatabase, ref, push, set } from "firebase/database";

// Create a new post reference with an auto-generated id
const db = getDatabase();
const postListRef = ref(db, 'posts');
const newPostRef = push(postListRef);
set(newPostRef, {
    // ...
});

Web

// Create a new post reference with an auto-generated id
var postListRef = firebase.database().ref('posts');
var newPostRef = postListRef.push();
newPostRef.set({
    // ...
});

Listen for child events

Child events are triggered in response to specific operations that happen to the children of a node from an operation such as a new child added through the push() method or a child being updated through the update() method.

Event Typical usage
child_added Retrieve lists of items or listen for additions to a list of items. This event is triggered once for each existing child and then again every time a new child is added to the specified path. The listener is passed a snapshot containing the new child's data.
child_changed Listen for changes to the items in a list. This event is triggered any time a child node is modified. This includes any modifications to descendants of the child node. The snapshot passed to the event listener contains the updated data for the child.
child_removed Listen for items being removed from a list. This event is triggered when an immediate child is removed.The snapshot passed to the callback block contains the data for the removed child.
child_moved Listen for changes to the order of items in an ordered list. child_moved events always follow the child_changed event that caused the item's order to change (based on your current order-by method).

Each of these together can be useful for listening to changes to a specific node in a database. For example, a social blogging app might use these methods together to monitor activity in the comments of a post, as shown below:

Web

import { getDatabase, ref, onChildAdded, onChildChanged, onChildRemoved } from "firebase/database";

const db = getDatabase();
const commentsRef = ref(db, 'post-comments/' + postId);
onChildAdded(commentsRef, (data) => {
  addCommentElement(postElement, data.key, data.val().text, data.val().author);
});

onChildChanged(commentsRef, (data) => {
  setCommentValues(postElement, data.key, data.val().text, data.val().author);
});

onChildRemoved(commentsRef, (data) => {
  deleteComment(postElement, data.key);
});

Web

var commentsRef = firebase.database().ref('post-comments/' + postId);
commentsRef.on('child_added', (data) => {
  addCommentElement(postElement, data.key, data.val().text, data.val().author);
});

commentsRef.on('child_changed', (data) => {
  setCommentValues(postElement, data.key, data.val().text, data.val().author);
});

commentsRef.on('child_removed', (data) => {
  deleteComment(postElement, data.key);
});

Listen for value events

While listening for child events is the recommended way to read lists of data, there are situations listening for value events on a list reference is useful.

Attaching a value observer to a list of data will return the entire list of data as a single snapshot which you can then loop over to access individual children.

Even when there is only a single match for the query, the snapshot is still a list; it just contains a single item. To access the item, you need to loop over the result:

Web

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase();
const dbRef = ref(db, '/a/b/c');

onValue(dbRef, (snapshot) => {
  snapshot.forEach((childSnapshot) => {
    const childKey = childSnapshot.key;
    const childData = childSnapshot.val();
    // ...
  });
}, {
  onlyOnce: true
});

Web

ref.once('value', (snapshot) => {
  snapshot.forEach((childSnapshot) => {
    var childKey = childSnapshot.key;
    var childData = childSnapshot.val();
    // ...
  });
});

This pattern can be useful when you want to fetch all children of a list in a single operation, rather than listening for additional child added events.

Sorting and filtering data

You can use the Realtime Database Query class to retrieve data sorted by key, by value, or by value of a child. You can also filter the sorted result to a specific number of results or a range of keys or values.

Sort data

To retrieve sorted data, start by specifying one of the order-by methods to determine how results are ordered:

Method Usage
orderByChild() Order results by the value of a specified child key or nested child path.
orderByKey() Order results by child keys.
orderByValue() Order results by child values.

You can only use one order-by method at a time. Calling an order-by method multiple times in the same query throws an error.

The following example demonstrates how you could retrieve a list of a user's top posts sorted by their star count:

Web

import { getDatabase, ref, query, orderByChild } from "firebase/database";
import { getAuth } from "firebase/auth";

const db = getDatabase();
const auth = getAuth();

const myUserId = auth.currentUser.uid;
const topUserPostsRef = query(ref(db, 'user-posts/' + myUserId), orderByChild('starCount'));

Web

var myUserId = firebase.auth().currentUser.uid;
var topUserPostsRef = firebase.database().ref('user-posts/' + myUserId).orderByChild('starCount');

This defines a query that when combined with a child listener synchronizes the client with the user's posts from the path in the database based on their user ID, ordered by the number of stars each post has received. This technique of using IDs as index keys is called data fan out, you can read more about it in Structure Your Database.

The call to the orderByChild() method specifies the child key to order the results by. In this case, posts are sorted by the value of their respective "starCount" child. Queries can also be ordered by nested children, in case you have data that looks like this:

"posts": {
  "ts-functions": {
    "metrics": {
      "views" : 1200000,
      "likes" : 251000,
      "shares": 1200,
    },
    "title" : "Why you should use TypeScript for writing Cloud Functions",
    "author": "Doug",
  },
  "android-arch-3": {
    "metrics": {
      "views" : 900000,
      "likes" : 117000,
      "shares": 144,
    },
    "title" : "Using Android Architecture Components with Firebase Realtime Database (Part 3)",
    "author": "Doug",
  }
},

In this case, we can order our list elements by values nested under the metrics key by specifying the relative path to the nested child in our orderByChild() call.

Web

import { getDatabase, ref, query, orderByChild } from "firebase/database";

const db = getDatabase();
const mostViewedPosts = query(ref(db, 'posts'), orderByChild('metrics/views'));

Web

var mostViewedPosts = firebase.database().ref('posts').orderByChild('metrics/views');

For more information on how other data types are ordered, see How query data is ordered.

Filtering data

To filter data, you can combine any of the limit or range methods with an order-by method when constructing a query.

Method Usage
limitToFirst() Sets the maximum number of items to return from the beginning of the ordered list of results.
limitToLast() Sets the maximum number of items to return from the end of the ordered list of results.
startAt() Return items greater than or equal to the specified key or value, depending on the order-by method chosen.
startAfter() Return items greater than the specified key or value depending on the order-by method chosen.
endAt() Return items less than or equal to the specified key or value, depending on the order-by method chosen.
endBefore() Return items less than the specified key or value depending on the order-by method chosen.
equalTo() Return items equal to the specified key or value, depending on the order-by method chosen.

Unlike the order-by methods, you can combine multiple limit or range functions. For example, you can combine the startAt() and endAt() methods to limit the results to a specified range of values.

Limit the number of results

You can use the limitToFirst() and limitToLast() methods to set a maximum number of children to be synced for a given event. For example, if you use limitToFirst() to set a limit of 100, you initially only receive up to 100 child_added events. If you have fewer than 100 items stored in your Firebase database, a child_added event fires for each item.

As items change, you receive child_added events for items that enter the query and child_removed events for items that drop out of it so that the total number stays at 100.

The following example demonstrates how example blogging app defines a query to retrieve a list of the 100 most recent posts by all users:

Web

import { getDatabase, ref, query, limitToLast } from "firebase/database";

const db = getDatabase();
const recentPostsRef = query(ref(db, 'posts'), limitToLast(100));

Web

var recentPostsRef = firebase.database().ref('posts').limitToLast(100);

This example only defines a query, to actually synchronize data it needs to have an attached listener.

Filter by key or value

You can use startAt(), startAfter(),endAt(), endBefore(), and equalTo() to choose arbitrary starting, ending, and equivalence points for queries. This can be useful for paginating data or finding items with children that have a specific value.

How query data is ordered

This section explains how data is sorted by each of the order-by methods in the Query class.

orderByChild

When using orderByChild(), data that contains the specified child key is ordered as follows:

  1. Children with a null value for the specified child key come first.
  2. Children with a value of false for the specified child key come next. If multiple children have a value of false, they are sorted lexicographically by key.
  3. Children with a value of true for the specified child key come next. If multiple children have a value of true, they are sorted lexicographically by key.
  4. Children with a numeric value come next, sorted in ascending order. If multiple children have the same numerical value for the specified child node, they are sorted by key.
  5. Strings come after numbers and are sorted lexicographically in ascending order. If multiple children have the same value for the specified child node, they are ordered lexicographically by key.
  6. Objects come last and are sorted lexicographically by key in ascending order.

orderByKey

When using orderByKey() to sort your data, data is returned in ascending order by key.

  1. Children with a key that can be parsed as a 32-bit integer come first, sorted in ascending order.
  2. Children with a string value as their key come next, sorted lexicographically in ascending order.

orderByValue

When using orderByValue(), children are ordered by their value. The ordering criteria are the same as in orderByChild(), except the value of the node is used instead of the value of a specified child key.

Detach listeners

Callbacks are removed by calling the off() method on your Firebase database reference.

You can remove a single listener by passing it as a parameter to off(). Calling off() on the location with no arguments removes all listeners at that location.

Calling off() on a parent listener does not automatically remove listeners registered on its child nodes; off() must also be called on any child listeners to remove the callback.

Next steps