1.0.6 • Published 10 months ago
@akshay-khapare/rn-multi-firebase-config v1.0.6
@akshay-khapare/rn-multi-firebase-config
A utility package for managing multiple Firebase projects within a single React Native application.
Installation
npm install @akshay-khapare/rn-multi-firebase-config
# or
yarn add @akshay-khapare/rn-multi-firebase-config
Prerequisites
Make sure you have the following packages installed in your React Native project:
npm install @react-native-firebase/app @react-native-firebase/firestore @react-native-firebase/auth @react-native-firebase/storage
Firebase Initialization
Initialize a single Firebase instance with custom configuration:
import { initializeFirebase, FirebaseConfig } from '@akshay-khapare/rn-multi-firebase-config';
const config: FirebaseConfig = {
apiKey: 'your-api-key',
authDomain: 'your-auth-domain',
projectId: 'your-project-id',
storageBucket: 'your-storage-bucket',
messagingSenderId: 'your-messaging-sender-id',
appId: 'your-app-id',
// Optional configurations
databaseURL: 'your-database-url',
measurementId: 'your-measurement-id'
};
// Initialize Firebase with a project name
await initializeFirebase('my-project', config);
// Configure Firestore settings
await configureFirestore('my-project');
Initialize multiple Firebase projects simultaneously:
import { initializeMultipleFirebaseProjects } from '@akshay-khapare/rn-multi-firebase-config';
const projectConfigs = [
{
name: 'project1',
config: {
apiKey: 'project1-api-key',
// ... other config properties
}
},
{
name: 'project2',
config: {
apiKey: 'project2-api-key',
// ... other config properties
}
}
];
// Initialize all projects at once
await initializeMultipleFirebaseProjects(projectConfigs);
Each project will be initialized with its own configuration and Firestore settings.
Example of handling initialization errors in your application:
async function initializeFirebaseProjects() {
try {
await initializeMultipleFirebaseProjects([
{
name: 'production',
config: productionConfig
},
{
name: 'development',
config: developmentConfig
}
]);
console.log('Firebase projects initialized successfully');
} catch (error) {
// Handle specific error cases
if (error.code === 'app/duplicate-app') {
console.error('Firebase project already exists');
} else if (error.code === 'app/invalid-credential') {
console.error('Invalid Firebase credentials');
} else {
console.error('Firebase initialization failed:', error);
}
// Rethrow or handle error based on your application needs
throw error;
}
}
Project Naming:
- Use consistent naming conventions for projects
- Avoid special characters in project names
- Consider using environment-based names (e.g., 'prod', 'dev', 'staging')
Configuration Management:
- Store Firebase configurations securely
- Use environment variables for sensitive data
- Never commit API keys to version control
Initialization Timing:
- Initialize Firebase as early as possible in your app lifecycle
- Ensure initialization is complete before using Firebase services
- Consider using a loading screen during initialization
Error Recovery:
- Implement retry logic for transient failures
- Have fallback configurations where appropriate
- Log initialization failures for debugging
Hooks Use Cases
const { listenToCollection } = useCollectionListener();
// Basic collection listening
const unsubscribe = listenToCollection<UserData>({
collection: 'users',
onData: (users) => {
console.log('Users updated:', users);
},
onError: (error) => {
console.error('Error listening to users:', error);
}
});
// Advanced querying with filters and ordering
const unsubscribeFiltered = listenToCollection<ProductData>({
collection: 'products',
firebaseProject: 'store',
where: [
['category', '==', 'electronics'],
['price', '<=', 1000]
],
orderBy: [
['price', 'asc'],
['name', 'asc']
],
limit: 10,
onData: (products) => {
console.log('Filtered products:', products);
}
});
// Cleanup when component unmounts
return () => {
unsubscribe();
unsubscribeFiltered();
};
const { listenToDocument } = useDocumentListener();
// Basic document listening
const unsubscribe = listenToDocument<UserProfile>({
collection: 'users',
doc: 'user123',
onData: (userData) => {
if (userData.exists) {
console.log('User data:', userData.data);
} else {
console.log('User document does not exist');
}
}
});
// Multi-project document listening
const unsubscribeMultiProject = listenToDocument<OrderData>({
collection: 'orders',
doc: 'order123',
firebaseProject: 'secondary',
onData: (orderData) => {
console.log('Order status:', orderData.data?.status);
},
onError: (error) => {
console.error('Error listening to order:', error);
}
});
// Cleanup
return () => {
unsubscribe();
unsubscribeMultiProject();
};
const { getData } = useFirestoreGet();
// Basic document retrieval
const fetchUserData = async () => {
const result = await getData<UserProfile>({
collection: 'users',
doc: 'user123',
firebaseProjectName: 'main'
});
if (result.exists) {
console.log('User data:', result.data);
}
};
// Type-safe data retrieval
interface ProductData {
name: string;
price: number;
stock: number;
}
const fetchProduct = async (productId: string) => {
const result = await getData<ProductData>({
collection: 'products',
doc: productId
});
if (result.exists && result.data) {
const { name, price, stock } = result.data;
console.log(`${name}: $${price} (${stock} in stock)`);
}
};
const { getQuery } = useFirestoreGetQuery();
// Basic query with filters
const fetchActiveUsers = async () => {
const users = await getQuery({
collection: 'users',
where: [
['status', '==', 'active'],
['lastLogin', '>=', new Date(Date.now() - 86400000)]
],
orderBy: [['lastLogin', 'desc']],
limit: 10
});
console.log('Active users:', users);
};
// Pagination with cursors
const fetchPaginatedProducts = async (lastProduct?: any) => {
const products = await getQuery({
collection: 'products',
orderBy: [['price', 'asc']],
startAfter: lastProduct,
limit: 20
});
return products;
};
// Complex query with multiple conditions
const searchProducts = async (category: string, minPrice: number, maxPrice: number) => {
const products = await getQuery({
collection: 'products',
where: [
['category', '==', category],
['price', '>=', minPrice],
['price', '<=', maxPrice],
['inStock', '==', true]
],
orderBy: [
['price', 'asc'],
['name', 'asc']
]
});
return products;
};
const { setData } = useFirestoreSet();
// Basic document creation
const createUser = async (userData: UserData) => {
const docId = await setData({
collection: 'users',
doc: 'user123',
data: userData,
addTimestamp: true
});
console.log('Created user:', docId);
};
// Document creation with merge
const updateUserPartial = async (userId: string, partialData: Partial<UserData>) => {
await setData({
collection: 'users',
doc: userId,
data: partialData,
merge: true,
addTimestamp: true
});
};
// Multi-project document creation
const createOrder = async (orderData: OrderData) => {
await setData({
collection: 'orders',
doc: `order_${Date.now()}`,
data: orderData,
firebaseProject: 'commerce',
addTimestamp: true
});
};
const { executeBatch, executeTransaction } = useFirestoreTransaction();
// Batch operations
const updateMultipleDocuments = async () => {
const operations = [
{
type: 'update' as const,
collection: 'products',
doc: 'prod1',
data: { stock: 10 },
addTimestamp: true
},
{
type: 'set' as const,
collection: 'inventory',
doc: 'inv1',
data: { lastChecked: new Date() },
merge: true
},
{
type: 'delete' as const,
collection: 'archived',
doc: 'old1'
}
];
const docIds = await executeBatch(operations);
console.log('Updated documents:', docIds);
};
// Complex transaction
const transferFunds = async (fromAccount: string, toAccount: string, amount: number) => {
await executeTransaction(async (transaction, firestore) => {
const fromRef = firestore().collection('accounts').doc(fromAccount);
const toRef = firestore().collection('accounts').doc(toAccount);
const fromSnapshot = await transaction.get(fromRef);
const toSnapshot = await transaction.get(toRef);
const newFromBalance = fromSnapshot.data()!.balance - amount;
const newToBalance = toSnapshot.data()!.balance + amount;
transaction.update(fromRef, { balance: newFromBalance });
transaction.update(toRef, { balance: newToBalance });
return { fromBalance: newFromBalance, toBalance: newToBalance };
});
};
const { updateData } = useFirestoreUpdate();
// Basic document update
const updateUserProfile = async (userId: string, updates: Partial<UserProfile>) => {
await updateData({
collection: 'users',
doc: userId,
data: updates,
addTimestamp: true
});
};
// Multi-project document update
const updateOrderStatus = async (orderId: string, status: string) => {
await updateData({
collection: 'orders',
doc: orderId,
data: { status, lastUpdated: new Date() },
firebaseProject: 'commerce',
addTimestamp: true
});
};
// Partial update with specific fields
const updateProductStock = async (productId: string, stockChange: number) => {
await updateData({
collection: 'products',
doc: productId,
data: {
stock: stockChange,
lastStockUpdate: new Date()
}
});
};
const { isExist } = useIsDocumentExist();
// Basic existence check
const checkUserExists = async (userId: string) => {
const exists = await isExist({
collection: 'users',
doc: userId
});
if (exists) {
console.log('User exists');
} else {
console.log('User not found');
}
};
// Multi-project existence check
const verifyOrder = async (orderId: string) => {
const orderExists = await isExist({
collection: 'orders',
doc: orderId,
firebaseProject: 'commerce'
});
return orderExists;
};
// Conditional operations based on existence
const createOrUpdateUser = async (userId: string, userData: UserData) => {
const exists = await isExist({
collection: 'users',
doc: userId
});
if (exists) {
// Update existing user
await updateData({ collection: 'users', doc: userId, data: userData });
} else {
// Create new user
await setData({ collection: 'users', doc: userId, data: userData });
}
};