Firebase Firestore integration in mobile app

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Firebase Firestore integration in mobile app
Medium
from 1 business day to 3 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1050
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Firebase Firestore Integration in Mobile Applications

Firestore is a document-oriented database with real-time listeners, offline caching, and flexible queries. Unlike RTDB, it supports composite indexes, where() on multiple fields, and orderBy() with pagination. However, it has its own pitfalls: onSnapshot without limit() on a growing collection gradually increases data volume on each tick until rendering slows down.

Data Structure: Collections and Subcollections

Basic structure for a social application:

users/{userId}
  ├── displayName: string
  ├── photoURL: string
  └── posts/{postId}     ← subcollection
      ├── text: string
      ├── createdAt: Timestamp
      └── likes: number

Subcollections are correct for data with unlimited quantity. Nested arrays in documents are incorrect for collections > 50 elements: Firestore limits document size to 1 MB, and the entire document is read even if you need one field.

Subscriptions and Pagination

import firestore from '@react-native-firebase/firestore';

// Real-time + pagination
const [posts, setPosts] = useState<Post[]>([]);
const [lastDoc, setLastDoc] = useState<FirebaseFirestoreTypes.DocumentSnapshot | null>(null);
const [loading, setLoading] = useState(false);

const fetchPage = useCallback(async () => {
  if (loading) return;
  setLoading(true);

  let query = firestore()
    .collection(`users/${userId}/posts`)
    .orderBy('createdAt', 'desc')
    .limit(20);

  if (lastDoc) query = query.startAfter(lastDoc);

  const snapshot = await query.get();
  const newPosts = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() } as Post));

  setPosts(prev => [...prev, ...newPosts]);
  setLastDoc(snapshot.docs[snapshot.docs.length - 1] ?? null);
  setLoading(false);
}, [lastDoc, loading, userId]);

// Real-time: only for top of feed (without pagination)
useEffect(() => {
  const unsubscribe = firestore()
    .collection(`users/${userId}/posts`)
    .orderBy('createdAt', 'desc')
    .limit(10)
    .onSnapshot(snapshot => {
      const fresh = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() } as Post));
      setPosts(prev => {
        // Combine new real-time data with paginated
        const ids = new Set(fresh.map(p => p.id));
        return [...fresh, ...prev.filter(p => !ids.has(p.id))];
      });
    });

  return () => unsubscribe();
}, [userId]);

Separate real-time (first page) and load more (pagination via get()). onSnapshot on the entire list with pagination is an antipattern: each collection change returns a complete new snapshot of the first N documents.

Offline Cache

// Enable before any Firestore access
await firestore().settings({
  cacheSizeBytes: firestore.CACHE_SIZE_UNLIMITED, // or specific size in bytes
  persistence: true, // enabled by default on mobile
});

When offline, onSnapshot continues to work, returning data from cache with snapshot.metadata.fromCache === true. Writes are buffered and sent on network recovery.

For explicit cache reading without network request:

const snapshot = await firestore()
  .collection('posts')
  .doc(postId)
  .get({ source: 'cache' }); // 'cache' | 'server' | 'default'

Composite Indexes

Queries with where + orderBy on different fields require a composite index. Firestore automatically suggests it on first error in development (console link). In production, configure indexes in firestore.indexes.json beforehand:

{
  "indexes": [
    {
      "collectionGroup": "posts",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "userId", "order": "ASCENDING" },
        { "fieldPath": "createdAt", "order": "DESCENDING" }
      ]
    }
  ]
}

Without an index, Firestore returns FAILED_PRECONDITION error. Index creation time ranges from minutes to hours on large collections.

Firestore Security Rules

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId}/posts/{postId} {
      allow read: if request.auth != null;
      allow write: if request.auth.uid == userId
        && request.resource.data.keys().hasAll(['text', 'createdAt'])
        && request.resource.data.text is string
        && request.resource.data.text.size() <= 2000;
    }
  }
}

Validate types and sizes in rules, not just client code.

Transactions and Batch Writes

// Transaction: atomic like toggle
await firestore().runTransaction(async transaction => {
  const postRef = firestore().doc(`posts/${postId}`);
  const userLikeRef = firestore().doc(`userLikes/${userId}_${postId}`);

  const [postSnap, likeSnap] = await Promise.all([
    transaction.get(postRef),
    transaction.get(userLikeRef),
  ]);

  if (likeSnap.exists()) {
    transaction.delete(userLikeRef);
    transaction.update(postRef, { likes: firestore.FieldValue.increment(-1) });
  } else {
    transaction.set(userLikeRef, { userId, postId, createdAt: firestore.FieldValue.serverTimestamp() });
    transaction.update(postRef, { likes: firestore.FieldValue.increment(1) });
  }
});

FieldValue.increment() is atomic increment without read-modify-write race. Without transaction, concurrent likes would have incorrect counter.

Estimation

Firestore with offline persistence, real-time subscriptions, pagination, and security rules: 2–4 weeks depending on data structure complexity.