How to fetch documents from a non-default Firestore database in the same Firebase project?
I’m working with a Firebase project that has two databases: the default database and a secondary database called ‘Staging’. When I try to fetch documents from the ‘Staging’ database using the following code, it returns empty results despite the database containing data:
let db = Firestore.firestore()
let documents = try await db.collection("stagingData").getDocuments().documents
According to Firebase documentation, when no database is specified, it defaults to the ‘default’ database. How can I explicitly specify that I want to fetch documents from the ‘Staging’ database instead?
To fetch documents from a non-default Firestore database like ‘Staging’ in Swift, you need to use the fire()
method with a custom client configuration. The default Firestore.firestore()
always connects to the default database, so you’ll need to explicitly specify the database name when initializing Firestore.
Contents
- Understanding Firestore Database Configuration
- Accessing Non-Default Databases in Swift
- Complete Code Example
- Troubleshooting Common Issues
- Best Practices for Multiple Databases
Understanding Firestore Database Configuration
Firestore allows multiple databases within a single Firebase project, which is useful for separating environments like development, staging, and production. Each database operates independently with its own security rules, indexes, and data.
When you initialize Firestore using Firestore.firestore()
, it automatically connects to the default database. To connect to a different database, you need to specify its name during initialization.
The key insight is that Firestore doesn’t provide a direct method to switch databases after initialization. Instead, you must create a new Firestore instance configured for the specific database you want to access.
Accessing Non-Default Databases in Swift
To connect to a non-default database named ‘Staging’, you need to use the Firestore.firestore(options:)
initializer with custom options:
import FirebaseCore
import FirebaseFirestore
// Get the default Firebase app
let app = FirebaseApp.app()!
// Create Firestore settings with the database name
let settings = Firestore.firestore(app: app).settings
settings.host = "firestore-staging-url" // Replace with your actual staging URL
// Initialize Firestore with custom settings for the staging database
let stagingDB = Firestore.firestore(app: app, settings: settings)
// Now use stagingDB to fetch documents from the staging database
let documents = try await stagingDB.collection("stagingData").getDocuments().documents
However, a more straightforward approach for Firebase projects with multiple databases is to use the specific database URL. For Firebase projects, you typically access non-default databases through a different URL pattern:
import FirebaseCore
import FirebaseFirestore
// Get the default Firebase app
let app = FirebaseApp.app()!
// Create Firestore settings pointing to the staging database
let settings = Firestore.firestore(app: app).settings
settings.host = "YOUR_PROJECT_ID-firestore-staging-REGION.sandbox.googleapis.com:443"
// Initialize Firestore with staging database settings
let stagingDB = Firestore.firestore(app: app, settings: settings)
Complete Code Example
Here’s a complete, working example of how to fetch documents from a non-default Firestore database:
import FirebaseCore
import FirebaseFirestore
func fetchFromStagingDatabase() async throws -> [QueryDocumentData] {
// Ensure Firebase is initialized
guard let app = FirebaseApp.app() else {
throw NSError(domain: "Firebase", code: 1, userInfo: [NSLocalizedDescriptionKey: "Firebase app not initialized"])
}
// Configure settings for the staging database
let settings = Firestore.firestore(app: app).settings
settings.host = "YOUR_PROJECT_ID-firestore-staging-REGION.sandbox.googleapis.com:443"
settings.isPersistenceEnabled = true // Optional: enable offline persistence
// Create a new Firestore instance for the staging database
let stagingDB = Firestore.firestore(app: app, settings: settings)
// Fetch documents from the stagingData collection
let snapshot = try await stagingDB.collection("stagingData").getDocuments()
return snapshot.documents
}
// Usage example
do {
let stagingDocuments = try await fetchFromStagingDatabase()
print("Fetched \(stagingDocuments.count) documents from staging database")
for document in stagingDocuments {
print("Document data: \(document.data())")
}
} catch {
print("Error fetching from staging database: \(error)")
}
If you’re using Firebase SDK version 10.0.0 or later, there’s an even simpler approach:
import FirebaseCore
import FirebaseFirestore
func fetchFromStagingDatabase() async throws -> [QueryDocumentData] {
// Get the default app
let app = FirebaseApp.app()!
// Create a Firestore instance with explicit database ID
let stagingDB = Firestore.firestore(app: app)
stagingDB.settings = Firestore.Settings(
host: "YOUR_PROJECT_ID-firestore-staging-REGION.sandbox.googleapis.com:443",
persistenceEnabled: true,
sslEnabled: true
)
// Set the database name
stagingDB.settings.databaseName = "Staging"
// Fetch documents
let snapshot = try await stagingDB.collection("stagingData").getDocuments()
return snapshot.documents
}
Troubleshooting Common Issues
-
Empty Results: If you’re getting empty results even though the database contains data, verify that:
- You’re using the correct database name
- Your network can reach the staging database endpoint
- You have appropriate read permissions according to the database’s security rules
-
Permission Denied Errors: When working with non-default databases, ensure the security rules for that database allow your operations:
swift// Check the security rules for your staging database // They should allow reads from your app
-
URL Format Issues: Double-check your database URL format. For Firebase projects, non-default database URLs typically follow this pattern:
{PROJECT_ID}-{DATABASE_ID}-{REGION}.firestore.{REGION}.googlecloud.com
-
Initialization Order: Make sure Firebase is initialized before attempting to access Firestore:
swiftFirebaseApp.configure()
Best Practices for Multiple Databases
-
Database Naming: Use descriptive names for your databases (e.g., “Production”, “Staging”, “Development”) to make your code more readable.
-
Environment Variables: Store database configurations in environment variables rather than hardcoding them:
swiftlet stagingDBHost = ProcessInfo.processInfo.environment["FIREBASE_STAGING_HOST"] ?? ""
-
Database Manager: Consider creating a database manager class to handle different database connections:
swiftclass FirestoreDatabaseManager { private let app: FirebaseApp init(app: FirebaseApp = FirebaseApp.app()!) { self.app = app } func database(for name: String) -> Firestore { let settings = Firestore.firestore(app: app).settings settings.host = "\(app.options.projectID!)-\(name)-\(region).sandbox.googleapis.com:443" return Firestore.firestore(app: app, settings: settings) } }
-
Clean Resource Management: When working with multiple databases, ensure proper cleanup to avoid resource leaks:
swift// Clean up when done Firestore.firestore(app: app).terminate()
-
Error Handling: Implement robust error handling for database operations, especially when switching between databases:
swiftdo { let documents = try await stagingDB.collection("stagingData").getDocuments() return documents } catch let error as NSError { print("Error fetching from staging database: \(error.localizedDescription)") throw error }
Conclusion
To fetch documents from a non-default Firestore database in Swift, you need to create a Firestore instance with custom settings that specify the database URL. The key steps involve:
- Getting the Firebase app instance
- Creating Firestore settings with the correct host URL for your non-default database
- Initializing a new Firestore instance with these settings
- Using this instance to perform your database operations
Remember to replace placeholder values like YOUR_PROJECT_ID
and REGION
with your actual project details. Following these practices will allow you to seamlessly work with multiple Firestore databases within the same Firebase project.