Google Cloud Firestore
File: firestore.py
Purpose: Serverless NoSQL database for Google Cloud deployments
Implementation Details
class FirestoreService(DatabaseService):
def __init__(self, project_id: str = None):
firebase_admin.initialize_app()
self.db = firestore.client()
Characteristics
- Storage: Cloud-native distributed database
- Persistence: Automatic with multi-region replication
- Performance: Low-latency worldwide access
- Revision Tracking: Placeholder
"firestore-rev"(uses server-side versioning) - Auto-initialization: Collections created on first write
Configuration
# .env or environment variables
DATABASE_PROVIDER=firestore
# Authentication via Firebase Admin SDK:
# - Set GOOGLE_APPLICATION_CREDENTIALS pointing to service account JSON
# - Or use Application Default Credentials in GCP
Authentication Setup
1. Create a Firebase Project
- Go to Firebase Console
- Create a new project or select an existing one
- Enable Firestore Database (Native mode recommended)
2. Generate Service Account Key
- Go to Project Settings > Service Accounts
- Click "Generate New Private Key"
- Download the JSON key file
- Store it securely (DO NOT commit to version control)
3. Set Environment Variable
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"
4. Alternative: Application Default Credentials
When running on Google Cloud Platform (GCP), you can use Application Default Credentials:
# No GOOGLE_APPLICATION_CREDENTIALS needed
# The SDK automatically uses the service account of the GCP resource
Special Considerations
1. Collection Auto-Creation
Collections are created implicitly on first document write:
- No need to pre-create collections
- No "create database" equivalent
2. Document Structure
Firestore uses collections and documents:
db_nameparameter maps to collection namedoc_idparameter is the document ID within that collection
3. ID Field
Implementation adds _id to returned documents:
def get(self, db_name: str, doc_id: str) -> Dict[str, Any]:
doc_ref = self.db.collection(db_name).document(doc_id)
doc = doc_ref.get()
data = doc.to_dict()
data["_id"] = doc.id # Add ID to document
return data
4. Listing Documents
Streams all documents in collection:
docs = self.db.collection(db_name).stream()
for doc in docs:
data = doc.to_dict()
data["_id"] = doc.id
documents.append(data)
5. Revision Information
Returns placeholder revision:
- Firestore handles versioning server-side
- Application doesn't need to track revisions
Production Recommendations
- Use security rules to restrict access
- Enable audit logging for compliance
- Use composite indexes for complex queries (if needed)
- Monitor quota usage (reads/writes/deletes)
- Consider Firestore Native mode over Datastore mode
- Use multi-region for high availability
Cost Considerations
- Charged per document read/write/delete
- Free tier: 50K reads, 20K writes, 20K deletes per day
- Storage: $0.18/GB/month
- Network egress charges apply
Cost Optimization Tips
- Minimize document reads by caching
- Use batch operations when possible
- Structure data to reduce document count
- Monitor usage in Firebase Console
Example Usage
from services.database_service import get_database_service
from config import Settings
import os
# Set credentials
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "/path/to/key.json"
settings = Settings(DATABASE_PROVIDER="firestore")
db = get_database_service(settings)
# Save a document
result = db.save("agents", "agent-1", {"name": "My Agent", "code": "..."})
print(result) # {"id": "agent-1", "rev": "firestore-rev"}
# Retrieve the document
agent = db.get("agents", "agent-1")
print(agent) # {"_id": "agent-1", "name": "My Agent", "code": "..."}
# List all agents
all_agents = db.list_all("agents")
# Delete the agent
db.delete("agents", "agent-1")
Common Operations
Setting Up Firestore Security Rules
// firestore.rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Allow authenticated users to read/write their own data
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
}
// Allow service account full access (for backend)
match /{document=**} {
allow read, write: if request.auth.token.email == "service-account@project.iam.gserviceaccount.com";
}
}
}
Deploy rules:
firebase deploy --only firestore:rules
Creating Composite Indexes
If you need complex queries, create indexes in Firebase Console or via CLI:
# firestore.indexes.json
{
"indexes": [
{
"collectionGroup": "agents",
"queryScope": "COLLECTION",
"fields": [
{"fieldPath": "created_at", "order": "DESCENDING"},
{"fieldPath": "name", "order": "ASCENDING"}
]
}
]
}
Deploy indexes:
firebase deploy --only firestore:indexes
Monitoring Usage
View usage in Firebase Console:
- Go to Firestore Database
- Click "Usage" tab
- Monitor reads, writes, deletes, and storage
Troubleshooting
Authentication Failed
Error: DefaultCredentialsError: Could not automatically determine credentials
Solutions:
- Set GOOGLE_APPLICATION_CREDENTIALS environment variable
- Verify the service account key file exists and is valid
- Check file permissions:
ls -l /path/to/key.json - Ensure the service account has Firestore permissions
Permission Denied
Error: PermissionDenied: Missing or insufficient permissions
Solutions:
- Verify service account has "Cloud Datastore User" role
- Check Firestore security rules
- Ensure Firestore is enabled in Firebase Console
- Grant appropriate IAM permissions in GCP Console
Document Not Found
Error: HTTPException: Document not found
Solutions:
- Verify the document exists in Firebase Console
- Check collection and document ID spelling
- Ensure you're using the correct Firebase project
Quota Exceeded
Error: ResourceExhausted: Quota exceeded
Solutions:
- Check usage in Firebase Console
- Upgrade to Blaze plan for higher quotas
- Optimize code to reduce reads/writes
- Implement caching to reduce database calls
Performance Tuning
Batch Operations
Use batch writes for multiple documents (up to 500 operations):
batch = self.db.batch()
# Add multiple operations
for i in range(100):
doc_ref = self.db.collection("agents").document(f"agent-{i}")
batch.set(doc_ref, {"name": f"Agent {i}"})
# Commit all at once
batch.commit()
Pagination
Implement pagination for large collections:
# Get first page
query = self.db.collection("agents").limit(100)
docs = query.stream()
# Get next page using last document as cursor
last_doc = None
for doc in docs:
last_doc = doc
# Process document
# Next page
if last_doc:
next_query = self.db.collection("agents").start_after(last_doc).limit(100)
next_docs = next_query.stream()
Server Timestamps
Use server timestamps instead of client timestamps:
from google.cloud.firestore import SERVER_TIMESTAMP
doc_ref.set({
"name": "Agent",
"created_at": SERVER_TIMESTAMP
})
Security Best Practices
- Service Account Security: Store credentials securely, never in version control
- IAM Roles: Use least privilege principle for service accounts
- Security Rules: Implement granular access control
- Audit Logging: Enable Cloud Audit Logs
- VPC Service Controls: Restrict Firestore access to specific networks
- Data Encryption: Firestore encrypts data at rest by default
Migration from Another Database
from services.database_service import get_database_service
from config import Settings
import os
# Source: CouchDB
source_settings = Settings(
DATABASE_PROVIDER="couchdb",
COUCHDB_URL="http://localhost:5984",
COUCHDB_USER="admin",
COUCHDB_PASSWORD="password"
)
source_db = get_database_service(source_settings)
# Target: Firestore
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "/path/to/key.json"
target_settings = Settings(DATABASE_PROVIDER="firestore")
target_db = get_database_service(target_settings)
# Migrate all collections
collections = ["agents", "deployments", "users", "sessions", "tickets", "demos"]
for collection in collections:
print(f"Migrating {collection}...")
documents = source_db.list_all(collection)
for doc in documents:
doc_id = doc.pop("_id")
# Remove source-specific fields
doc.pop("_rev", None)
# Save to Firestore
target_db.save(collection, doc_id, doc)
print(f" Migrated {doc_id}")
print(f"Completed {collection}: {len(documents)} documents")
Related Documentation
- Configuration - Database provider configuration
- Schema - Collection and document schemas
- Testing - Testing strategies
- Troubleshooting - Common issues and solutions
- Firebase Admin SDK Documentation
- Firestore Documentation
Last Updated: 2026-01-11