Firebase Firestore is a flexible NoSQL document database by Google with automatic real-time synchronization for mobile and web applications. Data is stored as collections and documents, each containing a set of fields with arbitrary structure. According to Google, 2026, Firestore supports multi-regional replication with automatic failover recovery. The SDK sends changes to the server via WebSocket connection with a latency of less than 100 milliseconds.
Key Takeaways
Firebase Firestore is a cloud NoSQL database launched by Google in 2019 as the successor to Realtime Database. Firestore is built on Google Cloud Spanner and Google Cloud Datastore infrastructure, providing strong data consistency within a single transaction and automatic multi-regional replication. The SDK supports Android, iOS, Web (JavaScript), Flutter, Kotlin Multiplatform, and Unity.
Firestore was announced at Google I/O 2017 as “Cloud Firestore” — a solution addressing the key limitations of Realtime Database: lack of complex query support, inability to scale data across multiple nodes, and weak consistency. According to Google (2026), Firestore processes over 1 trillion requests per day and is the default database for 80% of new Firebase projects. However, Realtime Database remains relevant for ultra-low latency scenarios (gaming, collaborative editing) due to its straightforward JSON structure.
Firestore is offered on a pay-as-you-go model with a generous free limit on the Spark plan: 1 GB of storage, 10 GB of network traffic per month, 50 thousand read operations, 20 thousand write operations, and 20 thousand delete operations per day. On the Blaze plan, all of the above is free, and overages are charged: $0.06 per 100 thousand read operations, $0.18 per 100 thousand write operations. According to Google (2026), 90% of projects stay within the free limit.
The data model of Firestore is organized hierarchically: the root contains collections, each collection contains documents, each document contains fields (primitive types, arrays, Map) and nested collections (subcollections). The nesting depth of collections is unlimited, but a document cannot directly contain another document — only through a reference (Reference type).
A collection is a container of documents with automatically generated or user-defined identifiers. Each document is a JSON-like object up to 1 MiB in size. Document fields can be strings, numbers, boolean values, arrays, Map, timestamps (Timestamp), GeoPoints, and references to other documents (Reference). The document size is limited to 1 MiB, including all field names.
| Firestore Field Type | Example | Indexed |
|---|---|---|
| String | “user@example.com” | Yes |
| Number | 42, 3.14 | Yes |
| Boolean | true, false | Yes |
| Array | [1, 2, 3] | Contains only |
| Map | {“nested”: “value”} | Yes (by keys) |
| Timestamp | 2026-07-03T12:00:00Z | Yes |
| Reference | users/user123 | Yes |
Firestore supports atomic transactions at the database level. A transaction can read and write multiple documents — Commit atomically applies all changes or none at all. Maximum of 500 operations per transaction, 60-second timeout. A batch write is a non-transactional atomic write operation without a read phase. Transactions are critical for financial operations, seat bookings, and inventory management.
Choosing between Firestore and Realtime Database depends on project requirements. Both databases are part of the Firebase ecosystem, provide real-time synchronization, and are available on all platforms, but fundamentally differ in data model, scaling, and pricing.
Realtime Database stores data in a single JSON tree, which is convenient for simple structures but makes scaling difficult with nesting deeper than 3 levels. Firestore uses a collection-document model with automatic sharding, allowing it to scale to millions of documents without performance degradation. According to Google (2026), Firestore supports up to 10 thousand concurrent connections to a single collection without speed loss, while Realtime Database supports up to 200 thousand connections to a single instance.
Realtime Database is billed based on data transferred (bytes downloaded) and the number of concurrent connections. Firestore is billed by the number of operations (read, write, delete). For applications with frequent small updates (chat, notifications), Firestore is usually more cost-effective — each write operation has a fixed price regardless of data size. For applications with infrequent reads of large data volumes, Realtime Database may be cheaper.
Google’s recommendation (2026): use Firestore as the default database for new projects, and Realtime Database for games and applications where minimal latency (under 50 ms) and flat data structure are critical. Both databases can work simultaneously in the same project.
Firestore queries are executed against collections or collection groups with filtering, sorting, and limits. Unlike Realtime Database, where each query traverses the entire JSON tree with client-side filtering, Firestore executes all queries on the server using pre-created indexes. This guarantees that query complexity depends only on the result size, not on the collection size.
Firestore supports filtering by one or multiple fields (equality, range, in, array-contains, array-contains-any), ascending and descending sorting, limits, and cursors for pagination. Limitations: compound queries with filtering on different fields (where price > 10 AND where category == “books”) require a composite index; OR queries are prohibited (use in and array-contains-any instead), and inequality queries on different fields are not allowed.
data class Product(
val name: String = "",
val category: String = "",
val price: Double = 0.0,
val inStock: Boolean = false
)
suspend fun FirestoreRepository.queryProducts(): List<Product> {
return firestore
.collection("products")
.whereEqualTo("category", "electronics")
.whereGreaterThanOrEqualTo("price", 100.0)
.whereLessThan("price", 500.0)
.orderBy("price")
.limit(20)
.get()
.await()
.toObjects(Product::class.java)
}
Firestore automatically creates indexes for single fields — single-field queries work without any configuration. For queries with two or more fields (filtering + sorting), composite indexes are required. When a query is first sent, Firestore returns an error with a link to the console where the index can be created with one click. Maximum of 200 composite indexes per database. Indexes can be exported and imported via the Firebase CLI.
Connecting Firestore to an Android app is done standardly via Firebase BOM. After adding the firebase-firestore-ktx dependency, the FirebaseFirestore object is available via getInstance() — without additional keys or tokens. Firestore uses the same Firebase project as other services.
dependencies {
implementation(platform("com.google.firebase:firebase-bom:33.1.0"))
implementation("com.google.firebase:firebase-firestore-ktx")
}
// Initialization
val db = FirebaseFirestore.getInstance()
Firestore provides two read modes: one-time (get) and real-time (addSnapshotListener). One-time read retrieves a document once — useful for settings and configuration. A listener subscribes to changes — any document update automatically delivers updated data to all connected clients in real time. set() creates or overwrites a document, update() modifies only the specified fields without overwriting the entire document.
According to Google (2026), medium-sized applications (100 thousand DAU) with real-time Firestore consume about 5-10 GB of outgoing traffic per month. Using offline cache (Persistence Cache) reduces repeat downloads by 60-70%, as the SDK only loads changed documents when the connection is restored.
Persistence Cache is a built-in Firestore mechanism for working without internet access. The SDK automatically caches all read documents on the device (up to 500 MiB on Android). When the connection is lost, reads continue from the cache, and writes are queued. When the connection is restored, all pending operations are sent to the server, and the cache is synchronized with the server. For conflict control, use snapshot-metadata.hasPendingWrites and setOptions(ServerTimestampBehavior).
Security Rules is a declarative access control language for Firestore that executes on Google’s server before each read or write operation. Rules do not require server-side code — they are written in the Firebase console or via Firebase CLI and versioned through Git. Each operation is checked against the rules, and a violation returns a PERMISSION_DENIED error.
Firestore Security Rules consist of match blocks and allow expressions. match defines the path to a collection or document, allow specifies permitted operations (read, write, create, update, delete) and a condition — a JavaScript-like expression returning a boolean. Rules can check authentication (request.auth), request data (request.resource.data), existing data (resource.data), time (request.time), and path (request.path).
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if request.auth != null;
allow write: if request.auth.uid == userId;
}
match /products/{productId} {
allow read: if true;
allow create: if request.auth.token.role == "admin";
allow update: if resource.data.authorId == request.auth.uid;
}
}
}
Security Rules support type and value validation on the server side. You can prohibit writing if the price is negative or the name is empty. All checks are performed on Google’s server before writing — this guarantees data consistency regardless of the client (Android, iOS, Web, Admin SDK). Rules do not protect against malicious Admin SDK — it bypasses rules by design. For full protection, use Transaction Functions and Firebase Extensions.
Frequently Asked Questions
Firestore uses a document model with indexes and complex queries. Realtime Database stores data in a JSON tree and provides lower latency. Firestore is recommended for new projects.
Firestore automatically shards data across collections — no need to configure replication or sharding. The database handles millions of documents in a collection and thousands of concurrent connections without degradation.
Yes, use Firebase Console — the “Export to Firestore” feature converts the Realtime Database JSON structure into Firestore collections and documents in a few clicks. Nested nodes become nested collections.
Last write wins — by default, Firestore uses the “last write wins” policy for resolving conflicts during concurrent writes. For custom handling, use transactions with re-reading.
Free limit of the Spark plan: 1 GB of storage, 50 thousand read operations, and 20 thousand write operations per day. This is sufficient for MVPs and applications with low traffic.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also