DEV Community

Cover image for Detailed Guide to Firebase Realtime Database with a CRUD Tutorial using JavaScript
MohitSinghChauhan
MohitSinghChauhan

Posted on • Originally published at mohitdotexe.hashnode.dev

Detailed Guide to Firebase Realtime Database with a CRUD Tutorial using JavaScript

Firebase Realtime Database is a cloud-hosted NoSQL database that allows storing and syncing data between users in realtime. In this comprehensive guide, we will explore the key features of Firebase Realtime Database, understand how data is structured, and go through a detailed CRUD (Create, Read, Update, Delete) operations tutorial using JavaScript.

Overview of Firebase Realtime Database

Firebase Realtime Database is a flexible, scalable database for mobile and web application development. Here are some of its notable features:

  • Data is stored as JSON and synchronized in realtime to every connected client.

  • Realtime listeners can be used to subscribe to data changes.

  • Offline data persistence allows apps to continue functioning while offline.

  • Built-in security rules and authentication helps secure data access.

  • Integrates seamlessly with other Firebase services like Analytics, Crashlytics, etc.

  • Usage-based pricing - pay only for the resources you use.

The database uses key-value pairs to store data. The keys are strings while the values can be primitive types like numbers, strings, booleans or complex nested objects.

Data is organized into hierarchical JSON documents similar to below:

{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      "email": "alovelace@gmail.com"
    },
    "ghopper": { 
      "name": "Grace Hopper",
      "email": "ghopper@gmail.com"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

With this structure, related data is grouped into collections called nodes. For our user's data, "users" is the root node while "alovelace" and "ghopper" are child nodes.

Now let's understand how to get started with Firebase Realtime Database.

Getting Started with Firebase Realtime Database

To use Firebase Realtime Database, you need a Firebase account and a project. Follow these steps:

  1. Sign up for a free Firebase account at https://firebase.google.com

  2. Create a new Firebase project or select an existing one.

  3. In the Project Overview page, click on "Realtime Database" to access the database console.

  4. In the Database console, you can view stored data, add new nodes, edit/delete existing nodes.

  5. To start reading/writing from your app, you need to configure the SDK. Follow the setup guide to add Firebase to your app.

  6. Make sure to define security rules to restrict data access. We'll cover this later.

That completes the basic setup. The Firebase SDK provides a simple API to perform CRUD operations which we'll cover next.

Performing CRUD Operations

The Firebase SDKs provide simple methods to create, read, update and delete data. Let's go through each operation with examples:

1. Create Data

To write data, we use the set() method. It writes data to the specified database path or replacing any existing data at that path.

// Write "ada" node to "users/alovelace"
db.ref("users/alovelace").set({
  name: "Ada Lovelace",
  email: "alovelace@gmail.com"  
});

// Output
// Wrote data to https://<databaseName>.firebaseio.com/users/alovelace
Enter fullscreen mode Exit fullscreen mode

We can also pass complex nested objects to set() which will be stored as JSON.

2. Read Data

To read data, use the once() method. It fetches data from the specified path a single time.

const userRef = db.ref("users/alovelace");

userRef.once("value").then(snapshot => {
  const user = snapshot.val();
  console.log(user.name); // Ada Lovelace 
  console.log(user.email); // alovelace@gmail.com
});

// Output
// Ada Lovelace
// alovelace@gmail.com
Enter fullscreen mode Exit fullscreen mode

We can also use on() to setup persistent listeners that keep listening for data changes.

3. Update Data

To update existing data, use update(). It updates only the specified child keys.

// Update email
db.ref("users/alovelace").update({
  email: "ada@lovelace.com"  
});

// Output 
// Updated 1 key/value pair(s)
Enter fullscreen mode Exit fullscreen mode

To replace all data, use set() again.

4. Delete Data

To delete data, use remove(). It deletes all data at the specified path.

db.ref("users/alovelace").remove();

// Output
// Successfully removed data from https://<databaseName>.firebaseio.com/users/alovelace
Enter fullscreen mode Exit fullscreen mode

This covers the basic CRUD operations. But there are more advanced queries we can perform which we'll see next.

Querying Data

Firebase provides several methods to query and filter data. Some useful ones are:

  • orderByChild() - Sort results by a child key

  • limitToLast() - Get last n results

  • startAt()/endAt() - Fetch results in a range

  • equalTo() - Find items matching equality filter

Let's see some examples:

// Sort users by email alphabetically
const usersRef = db.ref("users");
usersRef.orderByChild("email").once("value", ...);

// Get last 5 chat messages
const chatsRef = db.ref("chats"); 
chatsRef.limitToLast(5).once("value", ...);

// Fetch users between ages 18 to 30 
usersRef.orderByChild("age")
         .startAt(18).endAt(30)
         .once("value", ...);

// Find user by email
usersRef.orderByChild("email")
        .equalTo("ada@lovelace.com")
        .once("value", ...);
Enter fullscreen mode Exit fullscreen mode

We can combine multiple query methods together to create complex queries.

Using Wildcards for Flexible Queries

When building queries, we can use wildcards in our database references to match flexible data paths.

Some examples:

// Get posts for all users 
const postsRef = db.ref("users/*/posts");
// This will match /users/uid1/posts, /users/uid2/posts etc

// Find recent posts across users
const recentPostsRef = db.ref("users/*/posts/*"); 
recentPostsRef.limitToLast(10).once(...)
// This will return the 10 most recent posts under any user

// Get chat messages for any room 
const roomMessagesRef = db.ref("chatrooms/-*/messages");
// This will match /chatrooms/room1/messages, /chatrooms/room2/messages etc
Enter fullscreen mode Exit fullscreen mode

Wildcards provide a flexible and powerful way to query data without knowing the exact node structure beforehand. They are useful for common social app patterns like:

  • Fetching recent posts across users

  • Getting messages for any chatroom

  • Searching data by value across nodes

Refer to the Firebase docs for more details and examples on using wildcards for queries. They help build robust applications by querying data in a dynamic way.

Now that we have covered basic operations, let's discuss how to structure your data.

Structuring Your Data

As we saw earlier, data in Firebase Realtime Database is organized into hierarchical JSON documents. But how we design this structure is crucial for performant apps. Here are some best practices for structuring data:

  • Organize data by collections/categories - Group related entities like users, posts, comments together as separate root nodes. Avoid a single large list of items.

  • Avoid nesting data too deep - Excessively nested data requires multiple roundtrips to fetch. Keep nesting levels shallow.

  • Mix shallow and deep paths - Use a mix of deep and shallow child nodes. Shallow nodes are ideal for frequent writes while deep paths are good for less frequent data.

  • Keep lists of items small - When storing collections like messages or posts, limit number of items per node to max 100-200 items.

  • Avoid saving large unstructured data - Store files like images or videos on cloud storage like Firebase Storage. Store only URLs in database.

  • Denormalize data when needed - Duplicate data across locations to avoid excessive joins.

By planning your data structure upfront keeping these points in mind, you can optimize your app for performance.

Next we need to learn how to secure our data by defining security rules.

Adding Security Rules

By default, any data in your Firebase database is readable and writeable by anyone. To restrict access, we define security rules on each node.

Some examples:

// Allow only authenticated users to read/write user data
"users": {
  ".read": "auth != null",
  ".write": "auth != null",

  "$userId": {
    // Allow only the user to read/write their data
    ".read": "auth.uid === $userId",
    ".write": "auth.uid === $userId",
  }
}

// Make "posts" readable by anyone but writeable only by admins
"posts": {
  ".read": true,
  ".write": "root.child('admins').hasChild(auth.uid)" 
}
Enter fullscreen mode Exit fullscreen mode

Rules are applied recursively. So a user can only read/write their data but not others'. These simple declarative rules make securing data easy.

For more examples, refer the Firebase documentation on security rules. Defining proper rules as your app evolves is crucial to data security.

Conclusion

So in this detailed guide, we looked at how to use Firebase Realtime Database for building realtime apps. Some key takeaways:

  • Data is stored as JSON documents and synced across clients in realtime.

  • Perform common CRUD operations using the intuitive Firebase SDK methods.

  • Structure your data keeping in mind considerations like nesting levels, list sizes, denormalization etc.

  • Secure access to data using declarative security rules based on authentication.

By leveraging the flexible APIs provided by Firebase Realtime Database, you can focus on creating engaging realtime apps without the backend headaches. Use the knowledge gained from this tutorial to start building your next Firebase-powered application today!

Don't forget to checkout my other gem articles.

Happy Coding ❤️

Top comments (0)