Firebase allows multiple databases in the same project.
This is beneficial when you want to share authentication with between databases.
It also allows you to setup separate database rules.
My use case was to create separate databases for our suite of internal tools that have the same user base, but completely different applications, user roles, and data structures.
I just made a few tweaks to the great work done by @mark.breuss and his team.
Here is the github rebo. Please note the README is still pointing to Mark’s version and must be copied as an anvil project to be utilized.
The major differences being talked to are here:
Client Side multi-database
Where the database
arguement refers to the database name in your firebase project
initialize_app(config:dict,database=None,enable_offline_cache=False,function_region=None,persistence='local_persistence')->None:
'''Initializes the firebase class for client side environments'''
anvil.js.report_all_exceptions(True)
#Check credentials input value
if not isinstance(config,dict):
raise ValueError('Credentials must be of type dict')
#initialize application
global proxy_firebase
global app
proxy_firebase = anvil.js.import_from("https://www.gstatic.com/firebasejs/10.4.0/firebase-app.js")
app = anvil.js.await_promise(proxy_firebase.initializeApp(config))
#Initialize sub modules
authentication.init(app,persistence)
firestore.init(app,database=database,enable_offline_cache=enable_offline_cache)
storage.init(app)
functions.init(app,region=function_region)
Server side Multi-database
The name
argument is the same database
arguement above , referencing your databse name in the firebase project.
The project_name
refers to the project name in your project settings. This is important because the admin sdk does not update _database_string_internal
path when changing database names. (That was a bugger to find)
def init_firebase_server(skd_config,bucket_id=None,name='default',project_name=None):
'''Intializes the serer side firestore sdk'''
import firebase_admin
from firebase_admin import credentials, firestore, storage
if bucket_id is None:
try:
app = firebase_admin.initialize_app(credentials.Certificate(skd_config),name=name)
except ValueError as e:
if "already exist" not in str(e):
raise e
app = firebase_admin.get_app(name=name)
print(app.name)
firestore_client = firestore.client(app=app)
if name != 'default':
firestore_client._database_string_internal = (f"projects/{project_name}/databases/{name}")
return firestore_client
else:
app = firebase_admin.initialize_app(credentials.Certificate(skd_config),{'storageBucket': bucket_id},name=name)
return firestore.client(app=app), storage.bucket()
GOTCHAS
Some gotchas I kept running into on the server side was that the firebase_admin.auth does not automatically recognize the application instance as being initialized if it is not the default database. Every time you call an auth
method, you must first call the get_app() function and pass it in as an argument. See below:
from firebase_admin import auth,get_app
app = get_app(name=database_name)
try:
fire_user = auth.create_user(display_name=f'{user["name"]}',email=user['email'],app=app)
user_id = fire_user.uid
except Exception as e:
if "The email address is already in use by another account." in str(e):
return False