23 - NoSQL DBs - Firestore for Realtime Apps
Welcome to Day 23 of Learn GCP in 30 Days! Yesterday, you mastered relational SQL databases with Google Cloud SQL. Today, we step into the flexible, blazing-fast world of NoSQL Document Databases with Google Cloud Firestore.
Today's Goal Today, you will understand when to choose a NoSQL Document Database over a relational SQL database. You will learn the core architecture of Collections and Documents, understand Realtime Listeners that push live updates to web and mobile apps without WebSockets, write Python code to perform CRUD operations, and explore Firestore's generous Perpetual Free Tier!
๐ The Core Problem: When Rigid SQL Tables Slow Down Modern Apps
In traditional relational databases (like PostgreSQL or MySQL), every row in a table must strictly conform to a predefined schema.
What Existed Previously:
Developers built apps by defining strict SQL column structures. Whenever an application added a new feature (like adding social login handles or user preferences), engineers had to write risky database migrations (ALTER TABLE) and coordinate downtime.
Furthermore, building real-time applications (like chat apps, live scoreboards, or ride-sharing trackers) required building complex WebSocket servers and continuous database polling.
Problems Faced:
- ๐งฑ Rigid Schemas: Every record in a SQL table must have the exact same columns. Storing semi-structured or varying user profiles requires numerous nullable columns or messy JSON string fields.
- ๐ Polling Overhead: If 50,000 users open a chat app, each device polling the SQL database every 2 seconds creates 25,000 queries/second of idle traffic.
- ๐ถ Zero Offline Support: If a mobile phone loses network coverage in a tunnel, standard SQL client libraries immediately throw timeout exceptions and crash the user interface.
- โ๏ธ Scaling Bottlenecks: Relational databases scale vertically (bigger VMs). Handling millions of concurrent mobile connections requires complex connection pooling and sharding.
How Present Technology Solves It:
Google created Cloud Firestore (Serverless NoSQL Document Database):
- Flexible Document Model: Data is stored as JSON-like documents inside collections. Every document can have its own unique fields without database migrations.
- Realtime Push Listeners: Instead of clients repeatedly polling the server, Firestore uses persistent bi-directional streams. When a document changes in the cloud, Google pushes the update to all connected web and mobile devices in milliseconds.
- Automatic Offline Caching: The Firestore client SDK automatically caches data locally on iOS, Android, and Web. Users can write data while offline on an airplane, and Firestore seamlessly synchronizes the data when connectivity is restored!
- Massive Serverless Scalability: Firestore scales automatically to millions of concurrent connections with zero server provisioning and guarantees multi-region 99.999% availability.
๐๏ธ Real-World Analogy: Strict Spreadsheet vs. Dynamic Filing Folders
- Relational SQL Database = Strict Excel Spreadsheet:
- If Column D is
phone_number (Integer), every row must follow that rule. You cannot insert a row with an extra address object without adding new columns to the entire sheet.
- If Column D is
- Cloud Firestore NoSQL = Filing Cabinet with Flexible Index Cards:
- The filing drawer is a Collection (e.g.,
customers). - Inside the drawer, each index card is an independent Document (e.g.,
cust_101,cust_102). - Card 1 can have
nameandemail. - Card 2 can have
name,email,preferred_currency, and an array ofshipping_addresses. No schema migrations needed!
- The filing drawer is a Collection (e.g.,
๐งฉ Firestore Data Model: Collections, Documents & Subcollections
Firestore organizes data in a hierarchical tree:
1. Collections (Folders)
- A Collection is a container for documents.
- Collections cannot hold raw data directly (no raw strings or numbers); they contain only Documents.
- Collections are lightweight and created automatically as soon as you add the first document.
2. Documents (JSON-like Objects)
- A Document is a record that contains a set of key-value pairs (called Fields).
- Maximum document size is 1 Megabyte (plenty of room for rich user profiles or metadata).
- Each document has a unique identifier (e.g., auto-generated string like
al9xK90vQor custom ID likeuser_101).
3. Subcollections (Nested Hierarchies)
- Documents can contain nested Subcollections.
- For example,
/users/user_881/orders/order_9001allows you to store an order history that naturally belongs strictly touser_881.
4. Supported Data Types
Firestore supports rich, native data types:
- String, Number (integer or floating point), Boolean
- Timestamp (microsecond-precision cloud time)
- Geopoint (latitude and longitude coordinates)
- Map (nested JSON-like key-value objects)
- Array (lists of values or maps)
- Reference (pointers to other documents in the database)
- Null
โก 4 Architectural Superpowers of Firestore
1. Realtime Listeners (onSnapshot)
Instead of making one-off HTTP requests, your client code can attach a listener to a document or query. Whenever data changes in the cloud, your UI callback executes instantly.
2. Shallow Queries (Cost & Bandwidth Optimizer)
Queries in Firestore are shallow:
- If you query the
/userscollection to list all users, Firestore downloads only the user document fields. - It does NOT download their nested
/ordersor/messagessubcollections. This prevents massive bandwidth consumption.
3. Automatic Indexing
- Firestore automatically indexes every single field in every document.
- In traditional SQL, query performance slows down as your database grows from 1,000 to 10,000,000 rows unless you manually create indexes.
- In Firestore, query time is proportional to the size of the result set, NOT the size of your database! (Fetching 10 rows from a collection of 100,000,000 documents takes the exact same 15 milliseconds as fetching 10 rows from a collection of 10 documents).
4. ACID Multi-Document Transactions
Firestore is not an "eventually consistent" weak NoSQL database. It supports true ACID multi-document transactions and batch writes. If an operation updating 5 different documents encounters an error, all changes are atomically rolled back.
โ๏ธ Cloud SQL (Relational) vs. Cloud Firestore (NoSQL)
| Feature | ๐ฌ Google Cloud SQL | ๐ฅ Google Cloud Firestore |
|---|---|---|
| Data Model | Relational Tables (Rows & Columns) | Document NoSQL (Collections & Documents) |
| Schema | Strict, predefined schema | Flexible, dynamic schema |
| Scaling | Vertical scaling (Scale up VM CPU/RAM) | Automatic horizontal serverless scaling |
| Real-Time Sync | โ No (Requires client polling) | โ Yes (Built-in live push streams) |
| Offline Support | โ No | โ Yes (Automatic mobile/web client cache) |
| Billing Model | Charged by the Hour for running VM instances | Charged strictly by Operations (Reads/Writes/Deletes) |
| Best Used For | ERP, traditional financial systems, complex relational JOINs | Mobile apps, web apps, real-time gaming, chat, live inventory |
๐ The Firestore Perpetual Free Tier ($0 Forever)
One of the biggest advantages of Firestore is its Generous Perpetual Free Tier (part of Google Cloud's Free Tier that never expires, even after your $300 trial ends!):
- ๐ฝ Storage: 1 GB of stored data free forever.
- ๐ Document Reads: 50,000 reads per day free.
- โ๏ธ Document Writes: 20,000 writes per day free.
- ๐๏ธ Document Deletes: 20,000 deletes per day free.
- ๐ Egress Traffic: 10 GB / month free.
Zero Idle Cost Unlike Cloud SQL (which charges ~50/month just keeping a VM instance powered on), an idle Firestore database costs $0.0000 per month!
๐งช Hands-on Lab: Provisioning Firestore & Python CRUD Operations
In this lab, you will initialize a Cloud Firestore database in Native Mode, create collections and documents via the GCP Console, and run a Python script in Cloud Shell to query and listen to data.
Step 1: Initialize Cloud Firestore in Native Mode
Pathway A: Web Console (Click-by-Click)
- Open the Google Cloud Console (
https://console.cloud.google.com/). - In the top search bar, type Firestore and select Firestore Studio (or navigate to Navigation Menu Databases Firestore).
- If prompted to select a database mode, choose Firestore Native mode (Recommended for modern apps).
- Configure Database:
- Database ID:
(default)(leave as default). - Location type: Select Region.
- Region: Choose
us-central1 (Iowa)(or your closest region).
- Database ID:
- Click CREATE DATABASE. (Creation takes ~15 seconds).
Pathway B: Cloud Shell CLI
Open Google Cloud Shell and run:
# 1. Set environment variables
export PROJECT_ID=$(gcloud config get-value project)
export REGION="us-central1"
# 2. Create the default Firestore database in Native mode
gcloud firestore databases create \
--location=${REGION} \
--type=firestore-native
Step 2: Create a Collection and Document in the Web Console
Let's see how intuitive document creation is in the Cloud Console:
- In Firestore Studio, click + START COLLECTION.
- Collection ID:
products. Click Next. - Document ID: Leave as Auto-ID (or enter
prod_laptop_01). - Add fields to this document:
- Field:
name| Type:string| Value:MacBook Pro 16 - Click + Add field
- Field:
price| Type:number| Value:2499 - Click + Add field
- Field:
in_stock| Type:boolean| Value:true - Click + Add field
- Field:
tags| Type:array| Add values:"electronics","computers"
- Field:
- Click SAVE.
You now have your first NoSQL collection and document live in the cloud!
Step 3: Write a Python Script to Insert & Query Documents
Let's interact with Firestore programmatically using the official Google Cloud Python client library.
How Code Files are Created in this Lab (2 Ways)
- โก Fast 1-Click Way (Recommended): Simply copy & paste the
cat << 'EOF' ... EOFcommand block below directly into your terminal. It creates and saves the file automatically in 1 second! - ๐ Manual Way (If you want to edit code): You can also use
nano firestore_demo.py(Save:Ctrl+OEnter, Exit:Ctrl+X).
In Cloud Shell:
# 1. Install the official Google Cloud Firestore library
pip install google-cloud-firestore --quiet
# 2. Create the Python script
cat << 'EOF' > firestore_demo.py
import datetime
from google.cloud import firestore
# Initialize Firestore Client (auto-authenticates with Cloud Shell credentials)
db = firestore.Client()
print("๐ 1. Adding documents to 'customers' collection...")
# Add Document 1 with custom ID
db.collection("customers").document("cust_101").set({
"name": "Manikanta Kumar",
"email": "mani@example.com",
"membership": "premium",
"points": 450,
"created_at": datetime.datetime.now(datetime.timezone.utc)
})
# Add Document 2 with auto-generated ID
doc_ref = db.collection("customers").add({
"name": "Sarah Connor",
"email": "sarah@skynet.com",
"membership": "basic",
"points": 120,
"created_at": datetime.datetime.now(datetime.timezone.utc)
})
# Add Document 3
db.collection("customers").document("cust_103").set({
"name": "Bruce Wayne",
"email": "bruce@wayne-enterprises.com",
"membership": "premium",
"points": 9800,
"created_at": datetime.datetime.now(datetime.timezone.utc)
})
print("โ
Documents successfully written!\n")
print("๐ 2. Fetching all 'premium' customers...")
query = db.collection("customers").where(filter=firestore.FieldFilter("membership", "==", "premium"))
results = query.stream()
for doc in results:
data = doc.to_dict()
print(f" ๐ ID: {doc.id} | Name: {data['name']} | Points: {data['points']}")
print("\nโ๏ธ 3. Updating points for cust_101 (+50 points)...")
db.collection("customers").document("cust_101").update({
"points": firestore.Increment(50)
})
updated_doc = db.collection("customers").document("cust_101").get()
print(f" ๐ Updated cust_101 Points: {updated_doc.to_dict()['points']}")
EOF
# 3. Run the script
python3 firestore_demo.py
Expected Output:
๐ 1. Adding documents to 'customers' collection...
โ
Documents successfully written!
๐ 2. Fetching all 'premium' customers...
๐ ID: cust_101 | Name: Manikanta Kumar | Points: 450
๐ ID: cust_103 | Name: Bruce Wayne | Points: 9800
โ๏ธ 3. Updating points for cust_101 (+50 points)...
๐ Updated cust_101 Points: 500
Step 4: Verify in Firestore Studio
- Return to the Google Cloud Console Firestore Studio.
- Notice that the new
customerscollection appeared automatically! - Click on
cust_101to view its fields, types, and the atomically incrementedpoints: 500.
๐ก๏ธ Step 5: Credit Safety & Resource Teardown
Because Firestore operates on a Serverless Free Tier, storing a few test documents costs $0.00. However, to keep your project completely clean:
Pathway A: Web Console (Click-by-Click)
- In Firestore Studio, click on the
customerscollection. - Click the three dots
โฎat the top right of the collection column select Delete collection. - Repeat for the
productscollection.
Pathway B: Cloud Shell CLI
# Delete all documents in collections recursively
gcloud firestore operations list
gcloud firestore databases delete --database='(default)' --quiet
# Clean up local Python demo file
rm -f firestore_demo.py
๐ Day 23 Summary & Quick Reference
Core Architecture Takeaways
- Document NoSQL Model: Firestore stores semi-structured data as JSON-like documents grouped into collections with zero rigid schema constraints.
- Realtime Push Sync: Built-in listeners push database changes to web/mobile devices in milliseconds without custom WebSocket servers.
- Offline First: Automatic local client caching enables uninterrupted mobile app usage even in airplane mode.
- Shallow Queries: Querying a collection returns document data without downloading heavy nested subcollections.
- Perpetual Free Tier: 1 GB storage and 50,000 reads/day free forever with zero idle VM costs.
Quick Command Cheat Sheet
| Task | Command / Code |
|---|---|
| Create DB (Native) | gcloud firestore databases create --location=REGION --type=firestore-native |
| Delete Database | gcloud firestore databases delete --database='(default)' |
| Python Client Init | from google.cloud import firestore; db = firestore.Client() |
| Write Document | db.collection("col").document("doc_id").set({"key": "val"}) |
| Read Document | doc = db.collection("col").document("doc_id").get() |
| Filter Query | db.collection("col").where(filter=FieldFilter("status", "==", "active")).stream() |
| Atomic Increment | db.collection("col").document("doc_id").update({"views": firestore.Increment(1)}) |
Tomorrow, in Day 24, we enter the world of ultra-fast in-memory caching with Google Cloud Memorystore (Redis)โlearning how to achieve sub-millisecond query responses and reduce database loads by 90%!
โ 22 - Relational DBs - Cloud SQL (Postgres and MySQL) | Next Topic โ 24 - Caching - MemoryStore Redis