Firebase is a popular platform for managing real-time data and authentication in mobile and web applications. In this article, we will detail how to manage follow and unfollow operations using Firebase Realtime Database and Firestore. Additionally, we will show you how to optimize these operations using Firebase Cloud Functions.

Introduction
Following and unfollowing operations are commonly used features in social media applications. By following other users, individuals gain the ability to view their posts. These types of complex operations require specific database requests. Firestore and Realtime Database alone may not be sufficient for these tasks. To manage these operations efficiently and securely, we will use the tools provided by Firebase and create a complementary workflow.
Requirements
To follow this guide, make sure you have the following tools installed:
- Firebase CLI
- Node.js
- Flutter (optional)
- Firebase SDK for Flutter
Step 1: Setting Up Your Firebase Project
First, create a Firebase project and configure the necessary settings:
- Go to the Firebase Console and create a new project.
- Enable Firebase Authentication.
- Add Firestore and Realtime Database to your project.
- Connect your Flutter app to Firebase.
Step 2: Structuring Your Database
The structure of our database will be as follows:
Firestore
- users collection:
- followingCount: The number of users a user is following.
- followerCount: The number of followers a user has.
Realtime Database
- users:
- {userId}/following/{targetUserId}
- {userId}/followers/{targetUserId}
- posts:
- {postId}: Post information.
- userFlows:
- {userId}/{postId}: Posts the user is following.
Step 3: Firestore and Realtime Database Security Rules
Database security is crucial to ensure that only authorized users can access and modify the data. Add the following security rules:
Firestore Rules
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
}
}
Realtime Database Rules
{
"rules": {
"users": {
"$userId": {
".read": "auth != null && auth.uid == $userId",
".write": "auth != null && auth.uid == $userId",
"following": {
"$targetUserId": {
".write": "auth != null && auth.uid == $userId"
}
},
"followers": {
"$followerId": {
".write": "auth != null && auth.uid == $followerId"
}
}
}
},
"posts": {
"$postId": {
".read": "auth != null",
".write": "auth != null && data.child('authorId').val() == auth.uid"
}
},
"userFlows": {
"$userId": {
".read": "auth != null && auth.uid == $userId",
".write": "auth != null && auth.uid == $userId"
}
}
}
}
Step 4: Using Firebase Cloud Functions for Follow and Unfollow
Cloud Functions are ideal for performing complex operations on the server side from the client side. Below, we will show you how to implement follow and unfollow operations using Cloud Functions.
index.js File
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const firestore = admin.firestore();
const realtimeDatabase = admin.database();
exports.followUser = functions.https.onCall(async (data, context) => {
const userId = data.userId;
const targetUserId = data.targetUserId;
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'The function must be called while authenticated.');
}
try {
await firestore.runTransaction(async (transaction) => {
transaction.update(firestore.collection('users').doc(userId), {
followingCount: admin.firestore.FieldValue.increment(1)
});
transaction.update(firestore.collection('users').doc(targetUserId), {
followerCount: admin.firestore.FieldValue.increment(1)
});
});
const followUpdates = {};
followUpdates[`users/${userId}/following/${targetUserId}`] = admin.database.ServerValue.TIMESTAMP;
followUpdates[`users/${targetUserId}/followers/${userId}`] = admin.database.ServerValue.TIMESTAMP;
await realtimeDatabase.ref().update(followUpdates);
const flowUpdates = {};
const userPostsSnapshot = await realtimeDatabase.ref('posts').orderByChild('authorId').equalTo(targetUserId).once('value');
const userPosts = userPostsSnapshot.val();
if (userPosts) {
Object.keys(userPosts).forEach((postId) => {
flowUpdates[`userFlows/${userId}/${postId}`] = admin.database.ServerValue.TIMESTAMP;
});
await realtimeDatabase.ref().update(flowUpdates);
}
return { isSuccess: true };
} catch (e) {
console.error(e);
return { isSuccess: false };
}
});
exports.unfollowUser = functions.https.onCall(async (data, context) => {
const userId = data.userId;
const targetUserId = data.targetUserId;
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'The function must be called while authenticated.');
}
try {
await firestore.runTransaction(async (transaction) => {
transaction.update(firestore.collection('users').doc(userId), {
followingCount: admin.firestore.FieldValue.increment(-1)
});
transaction.update(firestore.collection('users').doc(targetUserId), {
followerCount: admin.firestore.FieldValue.increment(-1)
});
});
const unfollowUpdates = {};
unfollowUpdates[`users/${userId}/following/${targetUserId}`] = null;
unfollowUpdates[`users/${targetUserId}/followers/${userId}`] = null;
await realtimeDatabase.ref().update(unfollowUpdates);
const flowUpdates = {};
const userPostsSnapshot = await realtimeDatabase.ref('posts').orderByChild('authorId').equalTo(targetUserId).once('value');
const userPosts = userPostsSnapshot.val();
if (userPosts) {
Object.keys(userPosts).forEach((postId) => {
flowUpdates[`userFlows/${userId}/${postId}`] = null;
});
await realtimeDatabase.ref().update(flowUpdates);
}
return { isSuccess: true };
} catch (e) {
console.error(e);
return { isSuccess: false };
}
});
Deploying Functions
To deploy these functions to Firebase, follow these steps:
- Install Firebase CLI and log in:
npm install -g firebase-tools
firebase login
2. Initialize your Firebase project:
firebase init functions
3. Update your index.js file with the above code.
4. Deploy the functions:
firebase deploy --only functions
Step 5: Flutter Client Code
Finally, here’s an example of how to call these functions from your Flutter client:
import 'package:cloud_functions/cloud_functions.dart';
Future<void> followUser(String userId, String targetUserId) async {
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('followUser');
final results = await callable.call(<String, dynamic>{
'userId': userId,
'targetUserId': targetUserId,
});
if (results.data['isSuccess']) {
print('Follow successful');
} else {
print('Follow failed');
}
}
Future<void> unfollowUser(String userId, String targetUserId) async {
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('unfollowUser');
final results = await callable.call(<String, dynamic>{
'userId': userId,
'targetUserId': targetUserId,
});
if (results.data['isSuccess']) {
print('Unfollow successful');
} else {
print('Unfollow failed');
}
}
Conclusion
In this article, you learned how to manage follow and unfollow operations using Firebase Realtime Database and Firestore. You also saw how to optimize these operations using Firebase Cloud Functions. These methods allow you to efficiently and securely manage user interactions in your mobile and web applications.
By using the tools provided by Firebase, you can improve user experience and increase performance in your social media applications. We hope this guide helps you in your projects. Good luck!
Note: This text was written with the assistance of artificial intelligence.
Leave a Reply