
FirestoreAuthorCloudBrum editorial teamLast reviewedReading time~12 minLevelBeginner to advanced
Google Cloud · Databases · Serverless document database with realtime sync
Firestore is Google Cloud’s serverless document database, built so that a phone, a browser and a back end can work on the same data. You keep documents in collections, Google handles the scaling and replication, and a client can listen to a query and receive each change as it happens. Every query is answered from an index, so a compound query without one fails until you create it, and the cost of a query follows the number of documents it returns, not the size of the database. That also makes the bill easy to predict: in the Standard edition you pay for document reads, writes and deletes, plus storage.
On this page (17 sections)
Mind map: Firestore at a glance
Free to share. The downloaded image includes the CloudBrum link.
Key features (summary)
- Documents in collectionsA document holds fields such as strings, numbers, timestamps, maps and arrays, up to 1 MiB. Collections hold documents, and a document can have its own subcollections, so data can be nested to match how it is read.
- Queries that scale with the resultBecause every query is served from an index, how long it takes depends on how many documents it returns and not on how big the database has grown.
- Indexes, some of them yoursSingle-field indexes are created automatically. A query that combines fields, or filters and sorts on different fields, needs a composite index that you declare. Collection group queries search every collection with the same name.
- Realtime updatesA mobile or web client can attach a listener to a document or a query and receive the current result and then every change. The SDKs also cache data on the device, so an app keeps working offline and syncs later.
- Two ways to control accessSecurity Rules decide what a signed-in end user may read or write from a mobile or web client. Server code uses IAM roles instead, and server libraries don’t pass through the rules.
- Regional or multi-regionPick a regional location, such as Mumbai, or a multi-region one that survives the loss of a region. Reads are strongly consistent, and the location can’t be changed after the database is created.
- Recovery optionsPoint-in-time recovery lets you read or restore the data as it was at a minute in the last seven days. Scheduled backups, managed exports and delete protection cover the other ways to lose data.
- Two editionsStandard bills per document read, write and delete. Enterprise works with MongoDB drivers, allows larger documents, and bills in read units and write units by the number of bytes touched.
Basic concepts
- Document
- A set of named fields, stored under an ID inside a collection. A Native mode document can be up to 1 MiB.
- Collection and subcollection
- A collection is a container of documents. A document can contain subcollections, which makes paths like customers/c-100/orders/o-0001. Deleting a document doesn’t delete its subcollections.
- Reference
- A field that points to another document by its path, a bit like a foreign key that the database doesn’t enforce.
- Index
- A sorted list that lets a query find its documents. Single-field indexes are automatic. A composite index covers several fields in a given order and must be created before the query that needs it.
- Collection group query
- A query over every collection with a given ID, wherever it sits in the hierarchy. It needs a collection group index.
- Native mode and Datastore mode
- Native mode has the realtime listeners and the mobile and web SDKs. Datastore mode keeps the older Datastore API for server-only applications.
- Edition
- Standard or Enterprise, chosen when the database is created. They differ in features, limits and how operations are billed.
- Realtime listener
- A long-lived connection that pushes a document or query result to the client when it first loads and whenever it changes.
- Security Rules
- Declarative rules, written in their own language, that decide what a mobile or web client may read or write. Server libraries are controlled by IAM instead.
- Transaction
- A group of reads and writes that succeeds as a whole. Reads come first, and the function may run again if another writer changed the same documents.
- TTL policy
- A setting on a timestamp field that tells Firestore to delete documents once that time has passed. Deletion usually follows within a day.
- Point-in-time recovery (PITR)
- Keeps seven days of document versions, so you can read the data at an earlier minute or restore it into a new database.
- Aggregation query
- A count, sum or average computed on the server over the index entries, instead of reading every document.
How to create & configure it
Create a database, an index and safe defaults with the gcloud CLI
- Shape the data around the queriesList what the application asks: the newest orders of a customer, open orders, one order by ID. Keep together what is read together, and make a subcollection or a separate collection only when the data grows without limit.
- Create the database in the right placeChoose Native mode, the edition and a location near the users or the rest of the system. The location is permanent.
- Turn on protection at the startEnable delete protection and point-in-time recovery when you create the database, and add a backup schedule.
- Declare the composite indexesCreate an index for every query that combines an equality filter with a sort, or filters on several ranges. The error message of a failed query also links to the index it needs.
- Write with create and server timestampsUse create for inserts so that a retry can’t overwrite, and let the server set createdAt, so clocks on devices don’t matter.
- Page with cursors, not offsetsUse limit with start_after on the last document. An offset still bills you for every document it skips.
- Set a TTL policy for temporary dataStore an expiry timestamp on sessions, carts and tokens, and enable the policy for that field.
- Lock down accessGive server identities a role such as Cloud Datastore User. For clients, write Security Rules that check the signed-in user, and test them in the emulator.
- Watch usage and costCheck read and write counts in Cloud Monitoring after each release, because one new listener or one wide query can change the bill.
Tip: you can’t rename a collection, move a database to another location or change its edition in place. If you get one of those wrong, create a new database and move the data with an export and an import. See the code references for the same setup as Terraform.
Common issues after configuration
| Symptom | Likely cause | What to check / fix |
|---|---|---|
| FAILED_PRECONDITION: The query requires an index | The query combines fields in a way that no single-field index can answer | Open the link in the error message, or create the composite index with the field order the query uses, and wait until it finishes building. |
| PERMISSION_DENIED | From a client: the Security Rules reject the request. From a server: the identity lacks a Firestore role | Test the rule in the emulator with the same user, or grant the service account a role such as roles/datastore.user. |
| A document is rejected as too large | The document went past 1 MiB, often through an array or a map that keeps growing | Move the growing part to a subcollection, with one document per entry. |
| ABORTED: Too much contention on these documents | Several transactions kept changing the same documents | Keep transactions short, avoid one hot document, and retry with backoff. The client libraries retry for you a few times. |
| Writes to one document slow down | A single document is written many times a second, such as a global counter | Split the counter into several documents (shards) and add them up when you read. |
| Reads are higher than the number of screens shown | A listener re-reads results after a reconnect, a query returns more documents than the screen uses, or an offset skips documents that are still billed | Add limit, narrow the filters, use cursors for paging and avoid listeners on large result sets. |
| Latency rises when a collection gets busy | Sequential document IDs or a steadily increasing indexed value, like a timestamp, send writes to one range of the index | Use auto-generated IDs, and ramp up traffic to a new collection gradually, as the best-practice guide suggests. |
| Expired documents are still there | The TTL policy isn’t enabled on that field, the field isn’t a timestamp, or deletion hasn’t happened yet | Check the policy state, store a timestamp value, and expect deletion within about a day, not at the second. |
| Subcollection data is still there after a delete | Deleting a document doesn’t delete the collections under it | Use a recursive delete (the Firebase CLI or the client library’s recursive delete) or remove the subcollections yourself. |
| The database can’t be moved or switched to another edition | Location and edition are fixed when it is created | Create a new database in the right place and edition, export the data and import it, then switch the application over. |
| The bill is higher than expected | Wide queries, listeners on big result sets, offsets, or many small writes that could be batched | Open the usage report, find the collection with the most reads, and fix the query or the listener behind it. |
Use cases: when and where to use it
Good fit
- Mobile and web apps that need realtime updates and offline support
- Back ends for apps with profiles, chats, carts and settings stored as documents
- Serverless back ends on Cloud Run or Cloud Run functions, since there are no connections to pool
- Applications that read small sets of documents by key or by simple filters
- Teams that want a database with no capacity to plan and a free daily quota to start
- Applications that already use MongoDB drivers and want a managed option (Enterprise edition)
Consider something else
- Relational data, joins and SQL reporting → Cloud SQL or Cloud Spanner
- Analytics over large data → BigQuery, after an export
- Very high write volumes with simple key access, such as time series → Cloud Bigtable
- A cache for hot data → Memorystore
- Plain files and large blobs → Cloud Storage
- The same job on other clouds → compare Amazon DynamoDB and Azure Cosmos DB
Pros: why consider it?
Why teams choose it
- Nothing to size or patch, and no connection limits to manage
- Realtime listeners and offline caching in the mobile and web SDKs
- Strongly consistent reads, with replication handled for you
- Query time follows the result size, not the database size
- A free daily quota, which covers prototypes and small apps
- Point-in-time recovery, backups and delete protection built in
Trade-offs to weigh
- Queries must be supported by an index, and compound queries need composite indexes you create
- No joins and little aggregation, so reporting needs an export
- A document is capped at 1 MiB in Native mode
- Location and edition can’t be changed later
- Costs follow reads and writes, so chatty clients and wide listeners add up
- The Standard edition has no MongoDB compatibility, which is an Enterprise feature
Common threats, vulnerabilities & risks
| Risk | Severity | Why it matters | Mitigation |
|---|---|---|---|
| Open Security Rules | High | Rules such as allow read, write: if true leave the database open to anyone with the project’s public web configuration. | Write rules that check the signed-in user and the document, test them in the emulator and run the test suite in your pipeline. |
| Overbroad server roles | High | A service account with Owner or Editor can read, change and delete every document and the database itself. | Give each service the Cloud Datastore User role, or a custom role, and keep delete permissions with a small group. |
| No recovery plan | High | A bug or a bad migration can overwrite data, and delete protection doesn’t stop that. | Enable point-in-time recovery, schedule backups, and rehearse a restore into a new database. |
| A listener on a huge result | Medium | Every client that connects re-reads the whole result, which is slow for users and expensive for you. | Listen to narrow queries with a limit, and fetch big lists with normal paged queries. |
| Hot documents and counters | Medium | One document that every request updates becomes the limit of the whole feature. | Shard counters, batch writes, and keep frequently written fields out of widely read documents. |
| Growing arrays inside documents | Medium | A list that keeps growing reaches 1 MiB, and each update rewrites more data. | Use a subcollection with one document per entry. |
| Sensitive data in documents | Medium | Fields are readable to anyone the rules or roles allow, and they appear in exports and backups. | Keep personal and payment data to the minimum, restrict who can export, and use customer managed keys where required. |
| Unplanned indexes | Low–Med | Automatic indexing of every field adds storage and write cost for fields nobody queries. | Add single-field index exemptions for large or never-queried fields. |
| Single region for critical data | Low–Med | A regional location can be unavailable during a regional outage. | Choose a multi-region location for data that must stay available, and test failure handling in the client. |
Performance optimisation
- Index every query shape you use, and wait for new composite indexes to finish building before the release.
- Use limit and cursors (start_after), so each page returns only what the screen shows.
- Ask for only the fields you need with a projection (select) on server-side reads, to cut data transferred.
- Use auto-generated document IDs for write-heavy collections, and ramp traffic up to a new collection gradually.
- Batch writes (up to 500 per batch) or use the bulk writer for imports, instead of one request per document.
- Keep documents small, with the rarely read parts in a subcollection or a separate document.
- Use aggregation queries for counts and sums, instead of reading every matching document.
- Place the database in the same region as the compute that uses it, to remove round-trip time.
Reduce monthly cost
FinOps is the habit of managing cloud spend together with engineering. These are the FinOps checks that matter most for Firestore.
- Use limit and narrow filters, because every returned document is a billed read.
- Replace offsets with cursors, since skipped documents are billed too.
- Use aggregation queries (count, sum, average), which bill per 1,000 index entries instead of per document.
- Keep listeners on small, specific queries, and detach them when a screen closes.
- Add single-field index exemptions for big fields that are never queried, to cut storage and write cost.
- Set TTL policies to remove temporary data, and so reduce stored data.
- Compare Standard and Enterprise for your workload, since large reads are billed in 4 KiB units in Enterprise.
- Keep PITR and backups only on databases that need them, and set retention to match your rules.
- Use the free daily quota for development and small apps (one database per project gets it).
- Put a budget alert on the project and review the Firestore usage report weekly.
Figures here come from Google's pricing and documentation pages and published guides, read in October 2026. Prices differ by location and sources show rates only as examples, so confirm on the live page (see Pricing).
Security optimisation
- Write Security Rules that check request.auth, and test them with the emulator before each release.
- Give server identities IAM roles such as Cloud Datastore User, and use IAM Conditions to limit a role to one database where you need it.
- Use App Check so that only your genuine apps can call Firestore from clients.
- For production databases, switch on delete protection and point-in-time recovery.
- Use customer managed encryption keys (Cloud KMS) if your policy requires them. Data is encrypted at rest by default.
- Reach Firestore from private workloads with Private Google Access or Private Service Connect, and use VPC Service Controls for sensitive projects.
- Enable Data Access audit logs for Firestore where you need to see who read or changed data.
- Keep secrets and card data out of documents, and keep keys in Secret Manager.
- Restrict who may run exports, imports and database deletion, since these move or remove all data.
- Review Security Command Center findings and IAM recommendations for the project.
Governance & compliance
Guardrails
- A shared Terraform module that creates databases with delete protection, point-in-time recovery, a backup schedule and labels
- An organisation policy and IAM review that limits who may delete databases or export data
- Security Rules kept in the repository with tests that run in CI, and a review for every rules change
- A naming rule for collections and indexes, with the query that needs each composite index in a comment
Continuous checks (Cloud Asset Inventory, Security Command Center and your own scripts)
- A scheduled script that lists databases without delete protection, point-in-time recovery or a backup schedule (the C# tab audits the data, and gcloud describes the settings)
- Alerts on read and write rates per database and on listener counts
- An audit-log alert for database deletion, exports and IAM policy changes
- A periodic review of Security Rules against the current data model
- A monthly report of collections by reads, writes and storage
Cloud Audit Logs record who changed the database and, if enabled, who read data, Cloud Asset Inventory keeps the configuration history, Security Command Center reports posture, and Google’s compliance reports manager holds certifications. Google runs the service, replicates the data and encrypts it at rest. The data model, Security Rules, IAM roles, locations, backup choices and what is stored are yours.
Reduce carbon footprint
- Query for only what a screen needs, so fewer documents are read, sent and rendered.
- Use TTL for temporary data and index exemptions for large unqueried fields to keep stored data small.
- Keep the database near the compute and users it serves, to avoid long network paths.
- Detach listeners when screens close, so devices and servers stop doing work nobody sees.
- Where data rules and latency allow, choose a location with a lower-carbon grid, which Google marks in its location list.
- Google’s Carbon Footprint report is per project and service group, so quote no Firestore-only figure.
Pricing
Purchase options
Standard operations Per document read and write
About $0.30 per million reads and $0.90 per million writes, example rates for us-central1 as listed in Google’s page and recent guides. Deletes are billed separately, and rates differ by location.
Free daily quota 50,000 reads a day
Also 20,000 writes and 20,000 deletes a day for one database per project, with 1 GiB of storage and 10 GiB of outbound transfer a month in the Standard edition.
Enterprise operations Per read and write unit
From about $0.05 per million read units and $0.26 per million write units. Reads count in 4 KiB units and writes in 1 KiB units, so large documents cost more than one operation. Realtime updates have their own line.
Storage, recovery and transfer Per GiB-month, backup, egress
Stored data and its indexes are billed by size. PITR and backup storage, restores and network egress are billed separately.
What you actually pay for
- Document reads, writes and deletes in Standard, or read and write units in Enterprise
- Stored data and its index entries
- Point-in-time recovery and backup storage, and restores or clones
- Network egress out of the location
- Managed exports and imports, and TTL deletions as deletes
Google lists a daily free quota for one database per project, and rates differ by location. See the official Firestore pricing page and the Google Cloud Pricing Calculator.
Important code references
An orders application: a Native mode database in Mumbai with delete protection and point-in-time recovery, a composite index for the newest orders of a customer, a TTL policy, a daily backup, an export and a role for the API’s service account. The CLI tab builds it; Terraform describes the same resources. The Python tab inserts an order so that a retry is harmless, reads the newest orders, counts on the server and reserves stock in a transaction, and the C# tab does the same inserts, query and transaction. Replace the project, location and names with your own.
# Placeholders: replace the project, the location and the service account with your own.
PROJECT=my-project-id
LOCATION=asia-south1
DB=shop
SA=orders-api@$PROJECT.iam.gserviceaccount.com
gcloud services enable firestore.googleapis.com --project $PROJECT
# 1. A Native mode database in Mumbai. The location can't be changed later.
# Delete protection and point-in-time recovery (seven days of history) are switched on from the start.
gcloud firestore databases create --project $PROJECT --database $DB --location $LOCATION --type firestore-native \
--delete-protection --enable-pitr
# 2. The index behind "newest orders of one customer". Without it that query fails, and the error links to this exact index.
gcloud firestore indexes composite create --project $PROJECT --database $DB --collection-group orders \
--field-config field-path=customerId,order=ascending --field-config field-path=createdAt,order=descending
# 3. Time to live: Firestore deletes an order once the timestamp in its expiresAt field has passed.
gcloud firestore fields ttls update expiresAt --project $PROJECT --database $DB --collection-group orders --enable-ttl
# 4. A daily backup, kept for seven days, as a second line of defence next to point-in-time recovery.
gcloud firestore backups schedules create --project $PROJECT --database $DB --recurrence daily --retention 7d
# 5. Let the API's service account read and write documents, and nothing else.
gcloud projects add-iam-policy-binding $PROJECT --member serviceAccount:$SA --role roles/datastore.user
# 6. Check the settings that matter, then keep a copy of the orders collection outside Firestore.
gcloud firestore databases describe --project $PROJECT --database $DB
gcloud firestore export gs://$PROJECT-firestore-exports/$(date +%F) --project $PROJECT --database $DB --collection-ids orders
# Placeholders: pass in your own values. The ones below are examples.
variable "project" {
type = string
}
variable "location" {
type = string
default = "asia-south1"
}
variable "api_service_account" {
type = string # for example orders-api@my-project-id.iam.gserviceaccount.com
}
provider "google" {
project = var.project
}
resource "google_project_service" "firestore" {
service = "firestore.googleapis.com"
disable_on_destroy = false
}
resource "google_firestore_database" "shop" {
name = "shop"
location_id = var.location
type = "FIRESTORE_NATIVE"
database_edition = "STANDARD"
point_in_time_recovery_enablement = "POINT_IN_TIME_RECOVERY_ENABLED"
delete_protection_state = "DELETE_PROTECTION_ENABLED"
depends_on = [google_project_service.firestore]
}
# The index behind "newest orders of one customer".
resource "google_firestore_index" "orders_by_customer" {
database = google_firestore_database.shop.name
collection = "orders"
fields {
field_path = "customerId"
order = "ASCENDING"
}
fields {
field_path = "createdAt"
order = "DESCENDING"
}
}
# Delete an order when the timestamp in expiresAt has passed.
resource "google_firestore_field" "orders_ttl" {
database = google_firestore_database.shop.name
collection = "orders"
field = "expiresAt"
ttl_config {}
}
resource "google_firestore_backup_schedule" "daily" {
database = google_firestore_database.shop.name
retention = "604800s" # seven days
daily_recurrence {}
}
resource "google_project_iam_member" "api" {
project = var.project
role = "roles/datastore.user"
member = "serviceAccount:${var.api_service_account}"
}
# pip install google-cloud-firestore
# An insert that is safe to retry, the newest orders of a customer, a server-side count and an order plus a stock reduction in one transaction.
import datetime as dt
from google.api_core import exceptions
from google.cloud import firestore
from google.cloud.firestore_v1.base_query import FieldFilter
db = firestore.Client(database="shop") # the project and the credentials come from the environment
def place_order(order_id, customer_id, total):
"""create() fails when the document already exists, so a retried request can't overwrite an order. Returns False in that case."""
try:
db.collection("orders").document(order_id).create({
"customerId": customer_id, "status": "PLACED", "total": total,
"createdAt": firestore.SERVER_TIMESTAMP,
"expiresAt": dt.datetime.now(dt.timezone.utc) + dt.timedelta(days=90), # the TTL policy deletes the order after this time
})
return True
except exceptions.AlreadyExists:
return False
def latest_orders(customer_id, limit=10):
"""Needs the composite index on customerId (ascending) and createdAt (descending). Without it the call fails with a link to the index."""
query = (db.collection("orders")
.where(filter=FieldFilter("customerId", "==", customer_id))
.order_by("createdAt", direction=firestore.Query.DESCENDING)
.limit(limit))
return [{"id": doc.id, **doc.to_dict()} for doc in query.stream()]
def count_placed(customer_id):
"""The count runs on the server and is billed as one read per 1,000 index entries, not one read per document."""
query = (db.collection("orders")
.where(filter=FieldFilter("customerId", "==", customer_id))
.where(filter=FieldFilter("status", "==", "PLACED")))
return query.count().get()[0][0].value
@firestore.transactional
def _reserve(transaction, stock_ref, order_ref, order):
stock = stock_ref.get(transaction=transaction) # reads come before writes in a transaction
if not stock.exists or stock.get("onHand") < 1:
return False
transaction.update(stock_ref, {"onHand": firestore.Increment(-1)})
transaction.create(order_ref, order)
return True
def order_and_reserve(order_id, customer_id, sku, total):
"""Creates the order and takes one unit out of stock, or does neither. The function can run again if another writer
touches the same documents, so keep side effects such as emails out of it."""
order = {"customerId": customer_id, "status": "PLACED", "total": total, "createdAt": firestore.SERVER_TIMESTAMP}
return _reserve(db.transaction(), db.collection("stock").document(sku), db.collection("orders").document(order_id), order)
if __name__ == "__main__":
print("created:", place_order("o-0001", "c-100", 2499))
print("created again:", place_order("o-0001", "c-100", 2499))
print("newest orders:", [order["id"] for order in latest_orders("c-100")])
print("placed orders:", count_placed("c-100"))
db.collection("stock").document("sku-9").set({"onHand": 5})
print("reserved:", order_and_reserve("o-0002", "c-100", "sku-9", 999))
// dotnet add package Google.Cloud.Firestore
// An insert that is safe to retry, the newest orders of a customer, and an order plus a stock reduction in one transaction.
using Google.Cloud.Firestore;
using Grpc.Core;
string project = Environment.GetEnvironmentVariable("GOOGLE_CLOUD_PROJECT") ?? throw new InvalidOperationException("Set GOOGLE_CLOUD_PROJECT first.");
FirestoreDb db = new FirestoreDbBuilder { ProjectId = project, DatabaseId = "shop" }.Build();
Console.WriteLine($"created: {await PlaceOrder("o-0001", "c-100", 2499)}");
Console.WriteLine($"created again: {await PlaceOrder("o-0001", "c-100", 2499)}");
// Needs the composite index on customerId (ascending) and createdAt (descending).
QuerySnapshot newest = await db.Collection("orders").WhereEqualTo("customerId", "c-100").OrderByDescending("createdAt").Limit(10).GetSnapshotAsync();
foreach (DocumentSnapshot doc in newest.Documents)
Console.WriteLine($"{doc.Id}: {doc.GetValue<string>("status")}");
// One transaction: take a unit out of stock and create the order, or do neither.
DocumentReference stock = db.Collection("stock").Document("sku-9");
DocumentReference order = db.Collection("orders").Document("o-0002");
bool reserved = await db.RunTransactionAsync(async transaction =>
{
DocumentSnapshot snapshot = await transaction.GetSnapshotAsync(stock); // reads come before writes
if (!snapshot.Exists || snapshot.GetValue<long>("onHand") < 1) return false;
transaction.Update(stock, "onHand", FieldValue.Increment(-1));
transaction.Create(order, new Dictionary<string, object> { ["customerId"] = "c-100", ["status"] = "PLACED", ["total"] = 999, ["createdAt"] = FieldValue.ServerTimestamp });
return true;
});
Console.WriteLine($"reserved: {reserved}");
async Task<bool> PlaceOrder(string orderId, string customerId, int total)
{
try
{
await db.Collection("orders").Document(orderId).CreateAsync(new Dictionary<string, object>
{
["customerId"] = customerId, ["status"] = "PLACED", ["total"] = total,
["createdAt"] = FieldValue.ServerTimestamp, ["expiresAt"] = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(90))
});
return true;
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.AlreadyExists)
{
return false; // the order exists already, so a retry does no harm
}
}
Best videos to explore
Videos picked for this service. Each one is labelled with its source, opens on YouTube if you prefer, and loads only when you press play.
Interview questions
BasicWhat is Firestore?
It’s Google Cloud’s serverless document database. You store documents, which hold fields like strings, numbers and maps, in collections. Google scales and replicates it, queries are answered from indexes, and mobile and web clients can listen for live changes and keep working offline.
BasicWhat is the difference between a document and a collection?
A document is one record with named fields, identified by an ID. A collection is a group of documents. A document can in turn contain subcollections, so a path like customers/c-100/orders/o-0001 reads as collection, document, collection, document.
BasicHow is Firestore billed in the Standard edition?
By the number of document reads, writes and deletes, plus stored data and network egress, with a free daily quota for one database per project. There is no capacity to provision, so a quiet app costs very little, and a query that returns many documents is billed for each of them.
IntermediateWhen do you need a composite index, and what happens if it is missing?
A query that filters on one field and sorts on another, or that uses several range and sort conditions, needs a composite index that lists the fields in that order. Without it Firestore rejects the query with a FAILED_PRECONDITION error that includes a link to create the index. Single-field indexes are automatic.
IntermediateHow do Security Rules and IAM differ?
Security Rules guard access from mobile and web clients and can check the signed-in user and the data. IAM roles guard access from server code and from the console, and server client libraries aren’t subject to the rules. A secure design uses both: rules for clients, and narrow roles for the services.
IntermediateWhy is a counter that every request updates a problem, and how do you fix it?
A single document can only take limited sustained writes, so a global counter becomes the bottleneck and starts to fail with contention errors. Use a distributed counter: split it over several documents, increment one chosen at random, and add them up when you read. For counts of documents, an aggregation query counts on the server.
AdvancedHow would you model data for a chat or feed application?
Put each message in a subcollection of its conversation, so a conversation document stays small and a listener can follow the newest messages with a limit. Keep what a screen needs together, accept some duplication, and store counters and last-message previews on the parent. Add the composite indexes for each sort, and plan retention with a TTL policy.
AdvancedWhat are the trade-offs between the Standard and Enterprise editions?
Standard bills per document read and write and has the realtime SDK experience and a 1 MiB document limit. Enterprise adds compatibility with MongoDB drivers, a larger document limit and richer query features, and bills read and write units by bytes, so large documents and scans weigh more. Edition is chosen at creation, so test your access patterns on both before you commit.
AdvancedHow do you protect a production Firestore database from data loss and bad deployments?
Turn on delete protection, enable point-in-time recovery for the last seven days and add scheduled backups for longer history. Keep Security Rules and IAM changes in version control with tests, restrict who may export or delete, and rehearse a restore into a new database so you know how long it takes and what the application needs to switch over.
AdvancedA collection suddenly has high latency and errors when traffic spikes. What do you check?
Look for hotspotting: sequential document IDs or an indexed field that always increases, such as a timestamp, push writes to one part of the index. Use auto-generated IDs, ramp traffic to new collections gradually, avoid very hot single documents, and check for transactions that contend on the same documents. Then confirm the client is retrying with backoff.
Similar or alternative services on other clouds
| Capability | AWS | Azure | Google Cloud |
|---|---|---|---|
| Service | Amazon DynamoDB | Azure Cosmos DB (API for NoSQL) | Firestore |
| Data model | Tables of items, partition key and sort key | Containers of JSON items, partition key | Collections of documents, with subcollections |
| Capacity | On-demand, or provisioned with auto scaling | Request units: provisioned, autoscale or serverless | No capacity to set; billed per operation |
| Secondary indexes | Global and local indexes that you design | Every property indexed by default, with a policy to tune | Single-field indexes automatic, composite ones declared |
| Consistency | Eventual by default, strong per read | Five levels, session by default | Strongly consistent reads and queries |
| Multi-Region | Global tables | Multi-region replication and writes | Multi-region locations |
| Change feed | DynamoDB Streams, 24 hours | Change feed | Realtime listeners and event triggers |
| Recovery | Point-in-time recovery, 1 to 35 days | Continuous backup, 7 or 30 days | Point-in-time recovery, 7 days, and scheduled backups |
| How you are billed | Request units or capacity units, plus storage | Request units or throughput per hour, plus storage | Reads, writes and deletes, plus storage |
Document changes can start code through Eventarc triggers on Cloud Run or Cloud Run functions. Customer managed keys come from Cloud KMS, usage metrics and alerts live in Cloud Monitoring, and an export can be loaded into BigQuery for analysis.
Did you find this article helpful?
Your answer helps us improve this page for the next reader.
Sources and further reading
CloudBrum wrote this page in its own words, using the provider’s public documentation listed below as the reference. Features, limits, names and prices change, so confirm anything that matters on the provider’s own pages before you decide.
- Firestore overviewGoogle Cloud
- Firestore editionsGoogle Cloud
- Index types in FirestoreGoogle Cloud
- Firestore quotas and limitsGoogle Cloud
- Firestore best practicesGoogle Cloud
- Manage data retention with TTL policiesGoogle Cloud
- Point-in-time recovery in FirestoreGoogle Cloud
- Get started with Firestore Security RulesFirebase
- Firestore pricingGoogle Cloud
- Terraform google: google_firestore_databaseHashiCorp
