┌─────────────────────────────────────────────────────────┐
│ Votre application │
├─────────────────────────────────────────────────────────┤
│ SDK Firebase (Client) │
│ - Gère automatiquement les jetons d'authentification │
│ - Gère le cache hors ligne │
│ - Écouteurs en temps réel │
└─────────────────────────────────────────────────────────┘ │ │ HTTPS + WebSocket ▼
┌─────────────────────────────────────────────────────────┐
│ Backend Firebase │
├──────────────┬──────────────┬──────────────┬────────────┤
│ Service │ Base de │ Service │ Runtime │
│ d'Auth. │ données │ de Stock. │ des Fcts │
│ │ Firestore │ │ │
└──────────────┴──────────────┴──────────────┴────────────┘
┌─────────────────────────────────────────────────────────┐
│ Votre application │
├─────────────────────────────────────────────────────────┤
│ SDK Firebase (Client) │
│ - Gère automatiquement les jetons d'authentification │
│ - Gère le cache hors ligne │
│ - Écouteurs en temps réel │
└─────────────────────────────────────────────────────────┘ │ │ HTTPS + WebSocket ▼
┌─────────────────────────────────────────────────────────┐
│ Backend Firebase │
├──────────────┬──────────────┬──────────────┬────────────┤
│ Service │ Base de │ Service │ Runtime │
│ d'Auth. │ données │ de Stock. │ des Fcts │
│ │ Firestore │ │ │
└──────────────┴──────────────┴──────────────┴────────────┘
┌─────────────────────────────────────────────────────────┐
│ Votre application │
├─────────────────────────────────────────────────────────┤
│ SDK Firebase (Client) │
│ - Gère automatiquement les jetons d'authentification │
│ - Gère le cache hors ligne │
│ - Écouteurs en temps réel │
└─────────────────────────────────────────────────────────┘ │ │ HTTPS + WebSocket ▼
┌─────────────────────────────────────────────────────────┐
│ Backend Firebase │
├──────────────┬──────────────┬──────────────┬────────────┤
│ Service │ Base de │ Service │ Runtime │
│ d'Auth. │ données │ de Stock. │ des Fcts │
│ │ Firestore │ │ │
└──────────────┴──────────────┴──────────────┴────────────┘
// Dans la console Firebase > Paramètres du projet > Général
// Cliquez sur "Ajouter une application" > icône Web // Enregistrer l'application web
const firebaseConfig = { apiKey: "AIzaSyDxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", authDomain: "your-app.firebaseapp.com", projectId: "your-app", storageBucket: "your-app.appspot.com", messagingSenderId: "123456789012", appId: "1:123456789012:web:abc123def456"
}; // Initialiser Firebase
import { initializeApp } from 'firebase/app';
const app = initializeApp(firebaseConfig);
// Dans la console Firebase > Paramètres du projet > Général
// Cliquez sur "Ajouter une application" > icône Web // Enregistrer l'application web
const firebaseConfig = { apiKey: "AIzaSyDxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", authDomain: "your-app.firebaseapp.com", projectId: "your-app", storageBucket: "your-app.appspot.com", messagingSenderId: "123456789012", appId: "1:123456789012:web:abc123def456"
}; // Initialiser Firebase
import { initializeApp } from 'firebase/app';
const app = initializeApp(firebaseConfig);
// Dans la console Firebase > Paramètres du projet > Général
// Cliquez sur "Ajouter une application" > icône Web // Enregistrer l'application web
const firebaseConfig = { apiKey: "AIzaSyDxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", authDomain: "your-app.firebaseapp.com", projectId: "your-app", storageBucket: "your-app.appspot.com", messagingSenderId: "123456789012", appId: "1:123456789012:web:abc123def456"
}; // Initialiser Firebase
import { initializeApp } from 'firebase/app';
const app = initializeApp(firebaseConfig);
// build.gradle au niveau du projet
buildscript { dependencies { classpath 'com.google.gms:google-services:4.4.0' }
} // build.gradle au niveau de l'application
plugins { id 'com.google.gms.google-services'
}
// build.gradle au niveau du projet
buildscript { dependencies { classpath 'com.google.gms:google-services:4.4.0' }
} // build.gradle au niveau de l'application
plugins { id 'com.google.gms.google-services'
}
// build.gradle au niveau du projet
buildscript { dependencies { classpath 'com.google.gms:google-services:4.4.0' }
} // build.gradle au niveau de l'application
plugins { id 'com.google.gms.google-services'
}
import { createUserWithEmailAndPassword, getAuth, updateProfile
} from 'firebase/auth'; const auth = getAuth(app); async function signUp(email, password, displayName) { try { const userCredential = await createUserWithEmailAndPassword( auth, email, password ); // Définir le nom d'affichage await updateProfile(userCredential.user, { displayName: displayName }); console.log('Utilisateur créé :', userCredential.user.uid); return userCredential.user; } catch (error) { // Gérer les codes d'erreur spécifiques switch (error.code) { case 'auth/email-already-in-use': throw new Error('Cet e-mail est déjà enregistré'); case 'auth/weak-password': throw new Error('Le mot de passe doit contenir au moins 6 caractères'); case 'auth/invalid-email': throw new Error('Adresse e-mail invalide'); default: throw new Error("L'inscription a échoué : " + error.message); } }
}
import { createUserWithEmailAndPassword, getAuth, updateProfile
} from 'firebase/auth'; const auth = getAuth(app); async function signUp(email, password, displayName) { try { const userCredential = await createUserWithEmailAndPassword( auth, email, password ); // Définir le nom d'affichage await updateProfile(userCredential.user, { displayName: displayName }); console.log('Utilisateur créé :', userCredential.user.uid); return userCredential.user; } catch (error) { // Gérer les codes d'erreur spécifiques switch (error.code) { case 'auth/email-already-in-use': throw new Error('Cet e-mail est déjà enregistré'); case 'auth/weak-password': throw new Error('Le mot de passe doit contenir au moins 6 caractères'); case 'auth/invalid-email': throw new Error('Adresse e-mail invalide'); default: throw new Error("L'inscription a échoué : " + error.message); } }
}
import { createUserWithEmailAndPassword, getAuth, updateProfile
} from 'firebase/auth'; const auth = getAuth(app); async function signUp(email, password, displayName) { try { const userCredential = await createUserWithEmailAndPassword( auth, email, password ); // Définir le nom d'affichage await updateProfile(userCredential.user, { displayName: displayName }); console.log('Utilisateur créé :', userCredential.user.uid); return userCredential.user; } catch (error) { // Gérer les codes d'erreur spécifiques switch (error.code) { case 'auth/email-already-in-use': throw new Error('Cet e-mail est déjà enregistré'); case 'auth/weak-password': throw new Error('Le mot de passe doit contenir au moins 6 caractères'); case 'auth/invalid-email': throw new Error('Adresse e-mail invalide'); default: throw new Error("L'inscription a échoué : " + error.message); } }
}
import { signInWithEmailAndPassword, signOut
} from 'firebase/auth'; async function signIn(email, password) { try { const userCredential = await signInWithEmailAndPassword( auth, email, password ); const user = userCredential.user; // Obtenir le jeton d'identification pour les appels API const idToken = await user.getIdToken(); console.log('Jeton d\'authentification :', idToken); return user; } catch (error) { switch (error.code) { case 'auth/user-not-found': throw new Error('Aucun compte avec cet e-mail'); case 'auth/wrong-password': throw new Error('Mot de passe incorrect'); case 'auth/too-many-requests': throw new Error('Trop de tentatives. Réessayez plus tard'); default: throw new Error('La connexion a échoué'); } }
} async function logOut() { await signOut(auth); console.log('Utilisateur déconnecté');
}
import { signInWithEmailAndPassword, signOut
} from 'firebase/auth'; async function signIn(email, password) { try { const userCredential = await signInWithEmailAndPassword( auth, email, password ); const user = userCredential.user; // Obtenir le jeton d'identification pour les appels API const idToken = await user.getIdToken(); console.log('Jeton d\'authentification :', idToken); return user; } catch (error) { switch (error.code) { case 'auth/user-not-found': throw new Error('Aucun compte avec cet e-mail'); case 'auth/wrong-password': throw new Error('Mot de passe incorrect'); case 'auth/too-many-requests': throw new Error('Trop de tentatives. Réessayez plus tard'); default: throw new Error('La connexion a échoué'); } }
} async function logOut() { await signOut(auth); console.log('Utilisateur déconnecté');
}
import { signInWithEmailAndPassword, signOut
} from 'firebase/auth'; async function signIn(email, password) { try { const userCredential = await signInWithEmailAndPassword( auth, email, password ); const user = userCredential.user; // Obtenir le jeton d'identification pour les appels API const idToken = await user.getIdToken(); console.log('Jeton d\'authentification :', idToken); return user; } catch (error) { switch (error.code) { case 'auth/user-not-found': throw new Error('Aucun compte avec cet e-mail'); case 'auth/wrong-password': throw new Error('Mot de passe incorrect'); case 'auth/too-many-requests': throw new Error('Trop de tentatives. Réessayez plus tard'); default: throw new Error('La connexion a échoué'); } }
} async function logOut() { await signOut(auth); console.log('Utilisateur déconnecté');
}
import { GoogleAuthProvider, signInWithPopup
} from 'firebase/auth'; async function signInWithGoogle() { const provider = new GoogleAuthProvider(); // Demander des étendues supplémentaires provider.addScope('email'); provider.addScope('profile'); try { const result = await signInWithPopup(auth, provider); const user = result.user; // Accéder au jeton OAuth Google const credential = GoogleAuthProvider.credentialFromResult(result); const googleAccessToken = credential.accessToken; return user; } catch (error) { if (error.code === 'auth/popup-closed-by-user') { throw new Error("Connexion annulée"); } throw new Error('La connexion Google a échoué'); }
}
import { GoogleAuthProvider, signInWithPopup
} from 'firebase/auth'; async function signInWithGoogle() { const provider = new GoogleAuthProvider(); // Demander des étendues supplémentaires provider.addScope('email'); provider.addScope('profile'); try { const result = await signInWithPopup(auth, provider); const user = result.user; // Accéder au jeton OAuth Google const credential = GoogleAuthProvider.credentialFromResult(result); const googleAccessToken = credential.accessToken; return user; } catch (error) { if (error.code === 'auth/popup-closed-by-user') { throw new Error("Connexion annulée"); } throw new Error('La connexion Google a échoué'); }
}
import { GoogleAuthProvider, signInWithPopup
} from 'firebase/auth'; async function signInWithGoogle() { const provider = new GoogleAuthProvider(); // Demander des étendues supplémentaires provider.addScope('email'); provider.addScope('profile'); try { const result = await signInWithPopup(auth, provider); const user = result.user; // Accéder au jeton OAuth Google const credential = GoogleAuthProvider.credentialFromResult(result); const googleAccessToken = credential.accessToken; return user; } catch (error) { if (error.code === 'auth/popup-closed-by-user') { throw new Error("Connexion annulée"); } throw new Error('La connexion Google a échoué'); }
}
import { onAuthStateChanged } from 'firebase/auth'; // S'abonner aux changements d'état d'authentification
onAuthStateChanged(auth, (user) => { if (user) { // L'utilisateur est connecté console.log('Utilisateur :', user.email); // Rediriger vers le tableau de bord window.location.href = '/dashboard'; } else { // L'utilisateur est déconnecté console.log('Aucun utilisateur'); // Rediriger vers la page de connexion window.location.href = '/login'; }
});
import { onAuthStateChanged } from 'firebase/auth'; // S'abonner aux changements d'état d'authentification
onAuthStateChanged(auth, (user) => { if (user) { // L'utilisateur est connecté console.log('Utilisateur :', user.email); // Rediriger vers le tableau de bord window.location.href = '/dashboard'; } else { // L'utilisateur est déconnecté console.log('Aucun utilisateur'); // Rediriger vers la page de connexion window.location.href = '/login'; }
});
import { onAuthStateChanged } from 'firebase/auth'; // S'abonner aux changements d'état d'authentification
onAuthStateChanged(auth, (user) => { if (user) { // L'utilisateur est connecté console.log('Utilisateur :', user.email); // Rediriger vers le tableau de bord window.location.href = '/dashboard'; } else { // L'utilisateur est déconnecté console.log('Aucun utilisateur'); // Rediriger vers la page de connexion window.location.href = '/login'; }
});
import { sendEmailVerification } from 'firebase/auth'; async function sendVerificationEmail(user) { await sendEmailVerification(user); console.log('E-mail de vérification envoyé');
} // Vérifier l'état de la vérification
if (!auth.currentUser.emailVerified) { console.log('E-mail non vérifié'); // Restreindre l'accès
}
import { sendEmailVerification } from 'firebase/auth'; async function sendVerificationEmail(user) { await sendEmailVerification(user); console.log('E-mail de vérification envoyé');
} // Vérifier l'état de la vérification
if (!auth.currentUser.emailVerified) { console.log('E-mail non vérifié'); // Restreindre l'accès
}
import { sendEmailVerification } from 'firebase/auth'; async function sendVerificationEmail(user) { await sendEmailVerification(user); console.log('E-mail de vérification envoyé');
} // Vérifier l'état de la vérification
if (!auth.currentUser.emailVerified) { console.log('E-mail non vérifié'); // Restreindre l'accès
}
votre-projet (racine)
└── utilisateurs (collection) ├── userId123 (document) │ ├── nom: "Jean" │ ├── email: "[email protected]" │ └── publications (sous-collection) │ ├── postId1 (document) │ └── postId2 (document) └── userId456 (document)
votre-projet (racine)
└── utilisateurs (collection) ├── userId123 (document) │ ├── nom: "Jean" │ ├── email: "[email protected]" │ └── publications (sous-collection) │ ├── postId1 (document) │ └── postId2 (document) └── userId456 (document)
votre-projet (racine)
└── utilisateurs (collection) ├── userId123 (document) │ ├── nom: "Jean" │ ├── email: "[email protected]" │ └── publications (sous-collection) │ ├── postId1 (document) │ └── postId2 (document) └── userId456 (document)
import { getFirestore } from 'firebase/firestore'; const db = getFirestore(app);
import { getFirestore } from 'firebase/firestore'; const db = getFirestore(app);
import { getFirestore } from 'firebase/firestore'; const db = getFirestore(app);
import { collection, addDoc, setDoc, doc
} from 'firebase/firestore'; // Option 1 : ID auto-généré
async function createUser(userData) { const docRef = await addDoc(collection(db, 'users'), userData); console.log('Document écrit avec l\'ID :', docRef.id); return docRef.id;
} // Option 2 : ID personnalisé
async function createUserWithId(userId, userData) { await setDoc(doc(db, 'users', userId), userData); console.log('Document écrit avec l\'ID personnalisé :', userId);
} // Utilisation
const userId = await createUser({ name: 'Alice', email: '[email protected]', createdAt: new Date(), role: 'user'
});
import { collection, addDoc, setDoc, doc
} from 'firebase/firestore'; // Option 1 : ID auto-généré
async function createUser(userData) { const docRef = await addDoc(collection(db, 'users'), userData); console.log('Document écrit avec l\'ID :', docRef.id); return docRef.id;
} // Option 2 : ID personnalisé
async function createUserWithId(userId, userData) { await setDoc(doc(db, 'users', userId), userData); console.log('Document écrit avec l\'ID personnalisé :', userId);
} // Utilisation
const userId = await createUser({ name: 'Alice', email: '[email protected]', createdAt: new Date(), role: 'user'
});
import { collection, addDoc, setDoc, doc
} from 'firebase/firestore'; // Option 1 : ID auto-généré
async function createUser(userData) { const docRef = await addDoc(collection(db, 'users'), userData); console.log('Document écrit avec l\'ID :', docRef.id); return docRef.id;
} // Option 2 : ID personnalisé
async function createUserWithId(userId, userData) { await setDoc(doc(db, 'users', userId), userData); console.log('Document écrit avec l\'ID personnalisé :', userId);
} // Utilisation
const userId = await createUser({ name: 'Alice', email: '[email protected]', createdAt: new Date(), role: 'user'
});
import { getDoc, getDocs, query, where, orderBy, limit
} from 'firebase/firestore'; // Obtenir un seul document
async function getUser(userId) { const docRef = doc(db, 'users', userId); const docSnap = await getDoc(docRef); if (docSnap.exists()) { return docSnap.data(); } else { throw new Error('Utilisateur non trouvé'); }
} // Requête avec filtres
async function getUsersByRole(role) { const q = query( collection(db, 'users'), where('role', '==', role), orderBy('createdAt', 'desc'), limit(10) ); const querySnapshot = await getDocs(q); const users = []; querySnapshot.forEach((doc) => { users.push({ id: doc.id, ...doc.data() }); }); return users;
} // Utilisation
const adminUsers = await getUsersByRole('admin');
console.log('Utilisateurs administrateurs :', adminUsers);
import { getDoc, getDocs, query, where, orderBy, limit
} from 'firebase/firestore'; // Obtenir un seul document
async function getUser(userId) { const docRef = doc(db, 'users', userId); const docSnap = await getDoc(docRef); if (docSnap.exists()) { return docSnap.data(); } else { throw new Error('Utilisateur non trouvé'); }
} // Requête avec filtres
async function getUsersByRole(role) { const q = query( collection(db, 'users'), where('role', '==', role), orderBy('createdAt', 'desc'), limit(10) ); const querySnapshot = await getDocs(q); const users = []; querySnapshot.forEach((doc) => { users.push({ id: doc.id, ...doc.data() }); }); return users;
} // Utilisation
const adminUsers = await getUsersByRole('admin');
console.log('Utilisateurs administrateurs :', adminUsers);
import { getDoc, getDocs, query, where, orderBy, limit
} from 'firebase/firestore'; // Obtenir un seul document
async function getUser(userId) { const docRef = doc(db, 'users', userId); const docSnap = await getDoc(docRef); if (docSnap.exists()) { return docSnap.data(); } else { throw new Error('Utilisateur non trouvé'); }
} // Requête avec filtres
async function getUsersByRole(role) { const q = query( collection(db, 'users'), where('role', '==', role), orderBy('createdAt', 'desc'), limit(10) ); const querySnapshot = await getDocs(q); const users = []; querySnapshot.forEach((doc) => { users.push({ id: doc.id, ...doc.data() }); }); return users;
} // Utilisation
const adminUsers = await getUsersByRole('admin');
console.log('Utilisateurs administrateurs :', adminUsers);
import { updateDoc, increment, arrayUnion, arrayRemove
} from 'firebase/firestore'; async function updateUser(userId, updates) { const userRef = doc(db, 'users', userId); await updateDoc(userRef, updates);
} // Opérations atomiques
await updateUser('userId123', { loginCount: increment(1), tags: arrayUnion('premium', 'beta-tester'), lastLogin: new Date()
}); // Supprimer d'un tableau
await updateUser('userId123', { tags: arrayRemove('beta-tester')
});
import { updateDoc, increment, arrayUnion, arrayRemove
} from 'firebase/firestore'; async function updateUser(userId, updates) { const userRef = doc(db, 'users', userId); await updateDoc(userRef, updates);
} // Opérations atomiques
await updateUser('userId123', { loginCount: increment(1), tags: arrayUnion('premium', 'beta-tester'), lastLogin: new Date()
}); // Supprimer d'un tableau
await updateUser('userId123', { tags: arrayRemove('beta-tester')
});
import { updateDoc, increment, arrayUnion, arrayRemove
} from 'firebase/firestore'; async function updateUser(userId, updates) { const userRef = doc(db, 'users', userId); await updateDoc(userRef, updates);
} // Opérations atomiques
await updateUser('userId123', { loginCount: increment(1), tags: arrayUnion('premium', 'beta-tester'), lastLogin: new Date()
}); // Supprimer d'un tableau
await updateUser('userId123', { tags: arrayRemove('beta-tester')
});
import { deleteDoc } from 'firebase/firestore'; async function deleteUser(userId) { await deleteDoc(doc(db, 'users', userId)); console.log('Utilisateur supprimé');
}
import { deleteDoc } from 'firebase/firestore'; async function deleteUser(userId) { await deleteDoc(doc(db, 'users', userId)); console.log('Utilisateur supprimé');
}
import { deleteDoc } from 'firebase/firestore'; async function deleteUser(userId) { await deleteDoc(doc(db, 'users', userId)); console.log('Utilisateur supprimé');
}
import { onSnapshot } from 'firebase/firestore'; // Écouter un seul document
const unsubscribe = onSnapshot( doc(db, 'users', userId), (doc) => { console.log('Utilisateur mis à jour :', doc.data()); }, (error) => { console.error('Erreur d\'écoute :', error); }
); // Écouter les résultats d'une requête
const q = query(collection(db, 'posts'), where('published', '==', true)); const unsubscribeQuery = onSnapshot(q, (snapshot) => { const posts = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); console.log('Publications publiées :', posts);
}); // Arrêter d'écouter
unsubscribe();
unsubscribeQuery();
import { onSnapshot } from 'firebase/firestore'; // Écouter un seul document
const unsubscribe = onSnapshot( doc(db, 'users', userId), (doc) => { console.log('Utilisateur mis à jour :', doc.data()); }, (error) => { console.error('Erreur d\'écoute :', error); }
); // Écouter les résultats d'une requête
const q = query(collection(db, 'posts'), where('published', '==', true)); const unsubscribeQuery = onSnapshot(q, (snapshot) => { const posts = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); console.log('Publications publiées :', posts);
}); // Arrêter d'écouter
unsubscribe();
unsubscribeQuery();
import { onSnapshot } from 'firebase/firestore'; // Écouter un seul document
const unsubscribe = onSnapshot( doc(db, 'users', userId), (doc) => { console.log('Utilisateur mis à jour :', doc.data()); }, (error) => { console.error('Erreur d\'écoute :', error); }
); // Écouter les résultats d'une requête
const q = query(collection(db, 'posts'), where('published', '==', true)); const unsubscribeQuery = onSnapshot(q, (snapshot) => { const posts = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); console.log('Publications publiées :', posts);
}); // Arrêter d'écouter
unsubscribe();
unsubscribeQuery();
rules_version = '2';
service cloud.firestore { match /databases/{database}/documents { // Fonction d'aide function isAuthenticated() { return request.auth != null; } function isOwner(userId) { return request.auth.uid == userId; } // Collection des utilisateurs match /users/{userId} { allow read: if isAuthenticated(); allow create: if isAuthenticated() && isOwner(userId); allow update, delete: if isOwner(userId); } // Collection des publications match /posts/{postId} { allow read: if true; // Lecture publique allow create: if isAuthenticated(); allow update, delete: if resource.data.authorId == request.auth.uid; } // Sous-collection privée match /users/{userId}/private/{document} { allow read, write: if isOwner(userId); } }
}
rules_version = '2';
service cloud.firestore { match /databases/{database}/documents { // Fonction d'aide function isAuthenticated() { return request.auth != null; } function isOwner(userId) { return request.auth.uid == userId; } // Collection des utilisateurs match /users/{userId} { allow read: if isAuthenticated(); allow create: if isAuthenticated() && isOwner(userId); allow update, delete: if isOwner(userId); } // Collection des publications match /posts/{postId} { allow read: if true; // Lecture publique allow create: if isAuthenticated(); allow update, delete: if resource.data.authorId == request.auth.uid; } // Sous-collection privée match /users/{userId}/private/{document} { allow read, write: if isOwner(userId); } }
}
rules_version = '2';
service cloud.firestore { match /databases/{database}/documents { // Fonction d'aide function isAuthenticated() { return request.auth != null; } function isOwner(userId) { return request.auth.uid == userId; } // Collection des utilisateurs match /users/{userId} { allow read: if isAuthenticated(); allow create: if isAuthenticated() && isOwner(userId); allow update, delete: if isOwner(userId); } // Collection des publications match /posts/{postId} { allow read: if true; // Lecture publique allow create: if isAuthenticated(); allow update, delete: if resource.data.authorId == request.auth.uid; } // Sous-collection privée match /users/{userId}/private/{document} { allow read, write: if isOwner(userId); } }
}
// Au lieu de : where('status', '==', 'active') OU where('status', '==', 'pending') const activeQuery = query( collection(db, 'tasks'), where('status', '==', 'active')
); const pendingQuery = query( collection(db, 'tasks'), where('status', '==', 'pending')
); const [activeSnap, pendingSnap] = await Promise.all([ getDocs(activeQuery), getDocs(pendingQuery)
]); // Fusionner les résultats côté client
// Au lieu de : where('status', '==', 'active') OU where('status', '==', 'pending') const activeQuery = query( collection(db, 'tasks'), where('status', '==', 'active')
); const pendingQuery = query( collection(db, 'tasks'), where('status', '==', 'pending')
); const [activeSnap, pendingSnap] = await Promise.all([ getDocs(activeQuery), getDocs(pendingQuery)
]); // Fusionner les résultats côté client
// Au lieu de : where('status', '==', 'active') OU where('status', '==', 'pending') const activeQuery = query( collection(db, 'tasks'), where('status', '==', 'active')
); const pendingQuery = query( collection(db, 'tasks'), where('status', '==', 'pending')
); const [activeSnap, pendingSnap] = await Promise.all([ getDocs(activeQuery), getDocs(pendingQuery)
]); // Fusionner les résultats côté client
# Installer Firebase CLI
npm install -g firebase-tools # Connexion
firebase login # Initialiser les fonctions dans votre projet
firebase init functions # Sélectionner : JavaScript, ESLint oui, Express.js non
# Installer Firebase CLI
npm install -g firebase-tools # Connexion
firebase login # Initialiser les fonctions dans votre projet
firebase init functions # Sélectionner : JavaScript, ESLint oui, Express.js non
# Installer Firebase CLI
npm install -g firebase-tools # Connexion
firebase login # Initialiser les fonctions dans votre projet
firebase init functions # Sélectionner : JavaScript, ESLint oui, Express.js non
// functions/index.js
const { onRequest } = require('firebase-functions/v2/https');
const admin = require('firebase-admin'); admin.initializeApp();
const db = admin.firestore(); // Point d'accès public
exports.getPublicData = onRequest(async (req, res) => { res.set('Access-Control-Allow-Origin', '*'); try { const snapshot = await db.collection('public').get(); const data = snapshot.docs.map(doc => doc.data()); res.json({ success: true, data }); } catch (error) { res.status(500).json({ error: error.message }); }
}); // Point d'accès protégé (vérifier le jeton d'authentification)
exports.getUserProfile = onRequest(async (req, res) => { res.set('Access-Control-Allow-Origin', '*'); // Obtenir le jeton de l'en-tête Authorization const authHeader = req.headers.authorization || ''; const token = authHeader.split('Bearer ')[1]; if (!token) { return res.status(401).json({ error: 'Non autorisé' }); } try { // Vérifier le jeton const decodedToken = await admin.auth().verifyIdToken(token); const userId = decodedToken.uid; // Obtenir les données de l'utilisateur const userDoc = await db.collection('users').doc(userId).get(); if (!userDoc.exists) { return res.status(404).json({ error: 'Utilisateur non trouvé' }); } res.json({ success: true, data: { id: userId, ...userDoc.data() } }); } catch (error) { res.status(401).json({ error: 'Jeton invalide' }); }
});
// functions/index.js
const { onRequest } = require('firebase-functions/v2/https');
const admin = require('firebase-admin'); admin.initializeApp();
const db = admin.firestore(); // Point d'accès public
exports.getPublicData = onRequest(async (req, res) => { res.set('Access-Control-Allow-Origin', '*'); try { const snapshot = await db.collection('public').get(); const data = snapshot.docs.map(doc => doc.data()); res.json({ success: true, data }); } catch (error) { res.status(500).json({ error: error.message }); }
}); // Point d'accès protégé (vérifier le jeton d'authentification)
exports.getUserProfile = onRequest(async (req, res) => { res.set('Access-Control-Allow-Origin', '*'); // Obtenir le jeton de l'en-tête Authorization const authHeader = req.headers.authorization || ''; const token = authHeader.split('Bearer ')[1]; if (!token) { return res.status(401).json({ error: 'Non autorisé' }); } try { // Vérifier le jeton const decodedToken = await admin.auth().verifyIdToken(token); const userId = decodedToken.uid; // Obtenir les données de l'utilisateur const userDoc = await db.collection('users').doc(userId).get(); if (!userDoc.exists) { return res.status(404).json({ error: 'Utilisateur non trouvé' }); } res.json({ success: true, data: { id: userId, ...userDoc.data() } }); } catch (error) { res.status(401).json({ error: 'Jeton invalide' }); }
});
// functions/index.js
const { onRequest } = require('firebase-functions/v2/https');
const admin = require('firebase-admin'); admin.initializeApp();
const db = admin.firestore(); // Point d'accès public
exports.getPublicData = onRequest(async (req, res) => { res.set('Access-Control-Allow-Origin', '*'); try { const snapshot = await db.collection('public').get(); const data = snapshot.docs.map(doc => doc.data()); res.json({ success: true, data }); } catch (error) { res.status(500).json({ error: error.message }); }
}); // Point d'accès protégé (vérifier le jeton d'authentification)
exports.getUserProfile = onRequest(async (req, res) => { res.set('Access-Control-Allow-Origin', '*'); // Obtenir le jeton de l'en-tête Authorization const authHeader = req.headers.authorization || ''; const token = authHeader.split('Bearer ')[1]; if (!token) { return res.status(401).json({ error: 'Non autorisé' }); } try { // Vérifier le jeton const decodedToken = await admin.auth().verifyIdToken(token); const userId = decodedToken.uid; // Obtenir les données de l'utilisateur const userDoc = await db.collection('users').doc(userId).get(); if (!userDoc.exists) { return res.status(404).json({ error: 'Utilisateur non trouvé' }); } res.json({ success: true, data: { id: userId, ...userDoc.data() } }); } catch (error) { res.status(401).json({ error: 'Jeton invalide' }); }
});
firebase deploy --only functions:getUserProfile
firebase deploy --only functions:getUserProfile
firebase deploy --only functions:getUserProfile
async function getUserProfile(token) { const response = await fetch( 'https://us-central1-your-app.cloudfunctions.net/getUserProfile', { headers: { 'Authorization': `Bearer ${token}` } } ); const data = await response.json(); return data;
}
async function getUserProfile(token) { const response = await fetch( 'https://us-central1-your-app.cloudfunctions.net/getUserProfile', { headers: { 'Authorization': `Bearer ${token}` } } ); const data = await response.json(); return data;
}
async function getUserProfile(token) { const response = await fetch( 'https://us-central1-your-app.cloudfunctions.net/getUserProfile', { headers: { 'Authorization': `Bearer ${token}` } } ); const data = await response.json(); return data;
}
const { onDocumentWritten } = require('firebase-functions/v2/firestore'); // Déclencher lorsque le document utilisateur change
exports.onUserUpdate = onDocumentWritten( 'users/{userId}', async (event) => { const userId = event.params.userId; const before = event.data?.before?.data(); const after = event.data?.after?.data(); // Vérifier si l'e-mail a changé if (before?.email !== after?.email) { console.log(`L'e-mail de l'utilisateur ${userId} a changé : ${before?.email} → ${after?.email}`); // Envoyer un e-mail de notification await admin.auth().getUser(userId); // Ajouter votre logique d'e-mail ici } }
); // Déclencher lors de la création d'une nouvelle publication
exports.onNewPost = onDocumentWritten( 'posts/{postId}', async (event) => { const post = event.data?.after?.data(); if (!post) return; // Document supprimé // Vérifier s'il s'agit d'un nouveau document if (!event.data?.before?.exists) { console.log('Nouvelle publication créée :', post.title); // Notifier les abonnés const followersSnap = await admin.firestore() .collection('users') .where('following', 'array-contains', post.authorId) .get(); const notifications = followersSnap.docs.map(doc => ({ userId: doc.id, postId: event.params.postId, type: 'new_post', createdAt: admin.firestore.FieldValue.serverTimestamp() })); const batch = admin.firestore().batch(); notifications.forEach(notif => { const ref = admin.firestore().collection('notifications').doc(); batch.set(ref, notif); }); await batch.commit(); } }
);
const { onDocumentWritten } = require('firebase-functions/v2/firestore'); // Déclencher lorsque le document utilisateur change
exports.onUserUpdate = onDocumentWritten( 'users/{userId}', async (event) => { const userId = event.params.userId; const before = event.data?.before?.data(); const after = event.data?.after?.data(); // Vérifier si l'e-mail a changé if (before?.email !== after?.email) { console.log(`L'e-mail de l'utilisateur ${userId} a changé : ${before?.email} → ${after?.email}`); // Envoyer un e-mail de notification await admin.auth().getUser(userId); // Ajouter votre logique d'e-mail ici } }
); // Déclencher lors de la création d'une nouvelle publication
exports.onNewPost = onDocumentWritten( 'posts/{postId}', async (event) => { const post = event.data?.after?.data(); if (!post) return; // Document supprimé // Vérifier s'il s'agit d'un nouveau document if (!event.data?.before?.exists) { console.log('Nouvelle publication créée :', post.title); // Notifier les abonnés const followersSnap = await admin.firestore() .collection('users') .where('following', 'array-contains', post.authorId) .get(); const notifications = followersSnap.docs.map(doc => ({ userId: doc.id, postId: event.params.postId, type: 'new_post', createdAt: admin.firestore.FieldValue.serverTimestamp() })); const batch = admin.firestore().batch(); notifications.forEach(notif => { const ref = admin.firestore().collection('notifications').doc(); batch.set(ref, notif); }); await batch.commit(); } }
);
const { onDocumentWritten } = require('firebase-functions/v2/firestore'); // Déclencher lorsque le document utilisateur change
exports.onUserUpdate = onDocumentWritten( 'users/{userId}', async (event) => { const userId = event.params.userId; const before = event.data?.before?.data(); const after = event.data?.after?.data(); // Vérifier si l'e-mail a changé if (before?.email !== after?.email) { console.log(`L'e-mail de l'utilisateur ${userId} a changé : ${before?.email} → ${after?.email}`); // Envoyer un e-mail de notification await admin.auth().getUser(userId); // Ajouter votre logique d'e-mail ici } }
); // Déclencher lors de la création d'une nouvelle publication
exports.onNewPost = onDocumentWritten( 'posts/{postId}', async (event) => { const post = event.data?.after?.data(); if (!post) return; // Document supprimé // Vérifier s'il s'agit d'un nouveau document if (!event.data?.before?.exists) { console.log('Nouvelle publication créée :', post.title); // Notifier les abonnés const followersSnap = await admin.firestore() .collection('users') .where('following', 'array-contains', post.authorId) .get(); const notifications = followersSnap.docs.map(doc => ({ userId: doc.id, postId: event.params.postId, type: 'new_post', createdAt: admin.firestore.FieldValue.serverTimestamp() })); const batch = admin.firestore().batch(); notifications.forEach(notif => { const ref = admin.firestore().collection('notifications').doc(); batch.set(ref, notif); }); await batch.commit(); } }
);
const { onSchedule } = require('firebase-functions/v2/scheduler'); // Exécuter tous les jours à minuit UTC
exports.dailyCleanup = onSchedule('toutes les 24 heures', async (event) => { console.log('Exécution du nettoyage quotidien'); // Supprimer les anciennes notifications (plus de 30 jours) const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); const oldNotifs = await admin.firestore() .collection('notifications') .where('createdAt', '<', thirtyDaysAgo) .get(); const batch = admin.firestore().batch(); oldNotifs.forEach(doc => batch.delete(doc.ref)); await batch.commit(); console.log(`Supprimé ${oldNotifs.size} anciennes notifications`);
});
const { onSchedule } = require('firebase-functions/v2/scheduler'); // Exécuter tous les jours à minuit UTC
exports.dailyCleanup = onSchedule('toutes les 24 heures', async (event) => { console.log('Exécution du nettoyage quotidien'); // Supprimer les anciennes notifications (plus de 30 jours) const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); const oldNotifs = await admin.firestore() .collection('notifications') .where('createdAt', '<', thirtyDaysAgo) .get(); const batch = admin.firestore().batch(); oldNotifs.forEach(doc => batch.delete(doc.ref)); await batch.commit(); console.log(`Supprimé ${oldNotifs.size} anciennes notifications`);
});
const { onSchedule } = require('firebase-functions/v2/scheduler'); // Exécuter tous les jours à minuit UTC
exports.dailyCleanup = onSchedule('toutes les 24 heures', async (event) => { console.log('Exécution du nettoyage quotidien'); // Supprimer les anciennes notifications (plus de 30 jours) const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); const oldNotifs = await admin.firestore() .collection('notifications') .where('createdAt', '<', thirtyDaysAgo) .get(); const batch = admin.firestore().batch(); oldNotifs.forEach(doc => batch.delete(doc.ref)); await batch.commit(); console.log(`Supprimé ${oldNotifs.size} anciennes notifications`);
});
# Définir les variables d'environnement
firebase functions:config:set \ stripe.secret="sk_test_xxx" \ email.api_key="key_xxx" # Accéder aux fonctions
const config = require('firebase-functions/config');
const stripe = require('stripe')(config.stripe.secret);
# Définir les variables d'environnement
firebase functions:config:set \ stripe.secret="sk_test_xxx" \ email.api_key="key_xxx" # Accéder aux fonctions
const config = require('firebase-functions/config');
const stripe = require('stripe')(config.stripe.secret);
# Définir les variables d'environnement
firebase functions:config:set \ stripe.secret="sk_test_xxx" \ email.api_key="key_xxx" # Accéder aux fonctions
const config = require('firebase-functions/config');
const stripe = require('stripe')(config.stripe.secret);
// Console Firebase > Stockage > Règles
rules_version = '2';
service firebase.storage { match /b/{bucket}/o { // Dossier des téléchargements d'utilisateurs match /users/{userId}/{allPaths=**} { allow read: if true; // Lecture publique allow write: if request.auth.uid == userId; allow delete: if request.auth.uid == userId; } // Actifs publics match /public/{allPaths=**} { allow read: if true; allow write: if false; // Administrateur uniquement via la console Firebase } }
}
// Console Firebase > Stockage > Règles
rules_version = '2';
service firebase.storage { match /b/{bucket}/o { // Dossier des téléchargements d'utilisateurs match /users/{userId}/{allPaths=**} { allow read: if true; // Lecture publique allow write: if request.auth.uid == userId; allow delete: if request.auth.uid == userId; } // Actifs publics match /public/{allPaths=**} { allow read: if true; allow write: if false; // Administrateur uniquement via la console Firebase } }
}
// Console Firebase > Stockage > Règles
rules_version = '2';
service firebase.storage { match /b/{bucket}/o { // Dossier des téléchargements d'utilisateurs match /users/{userId}/{allPaths=**} { allow read: if true; // Lecture publique allow write: if request.auth.uid == userId; allow delete: if request.auth.uid == userId; } // Actifs publics match /public/{allPaths=**} { allow read: if true; allow write: if false; // Administrateur uniquement via la console Firebase } }
}
import { getStorage, ref, uploadBytesResumable, getDownloadURL
} from 'firebase/storage'; const storage = getStorage(app); async function uploadProfileImage(userId, file) { // Créer une référence de stockage const storageRef = ref(storage, `users/${userId}/profile/${file.name}`); // Télécharger le fichier const uploadTask = uploadBytesResumable(storageRef, file); return new Promise((resolve, reject) => { uploadTask.on( 'state_changed', (snapshot) => { // Suivre la progression const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log(`Téléchargement : ${progress.toFixed(0)}%`); }, (error) => { // Gérer les erreurs switch (error.code) { case 'storage/unauthorized': reject(new Error('Vous n\'avez pas la permission')); break; case 'storage/canceled': reject(new Error('Téléchargement annulé')); break; default: reject(new Error('Le téléchargement a échoué')); } }, async () => { // Téléchargement terminé const downloadURL = await getDownloadURL(uploadTask.snapshot.ref); console.log('Fichier disponible à :', downloadURL); resolve(downloadURL); } ); });
} // Utilisation
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0]; if (file) { const imageUrl = await uploadProfileImage(auth.currentUser.uid, file); // Enregistrer l'URL dans Firestore await updateDoc(doc(db, 'users', auth.currentUser.uid), { profileImage: imageUrl });
}
import { getStorage, ref, uploadBytesResumable, getDownloadURL
} from 'firebase/storage'; const storage = getStorage(app); async function uploadProfileImage(userId, file) { // Créer une référence de stockage const storageRef = ref(storage, `users/${userId}/profile/${file.name}`); // Télécharger le fichier const uploadTask = uploadBytesResumable(storageRef, file); return new Promise((resolve, reject) => { uploadTask.on( 'state_changed', (snapshot) => { // Suivre la progression const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log(`Téléchargement : ${progress.toFixed(0)}%`); }, (error) => { // Gérer les erreurs switch (error.code) { case 'storage/unauthorized': reject(new Error('Vous n\'avez pas la permission')); break; case 'storage/canceled': reject(new Error('Téléchargement annulé')); break; default: reject(new Error('Le téléchargement a échoué')); } }, async () => { // Téléchargement terminé const downloadURL = await getDownloadURL(uploadTask.snapshot.ref); console.log('Fichier disponible à :', downloadURL); resolve(downloadURL); } ); });
} // Utilisation
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0]; if (file) { const imageUrl = await uploadProfileImage(auth.currentUser.uid, file); // Enregistrer l'URL dans Firestore await updateDoc(doc(db, 'users', auth.currentUser.uid), { profileImage: imageUrl });
}
import { getStorage, ref, uploadBytesResumable, getDownloadURL
} from 'firebase/storage'; const storage = getStorage(app); async function uploadProfileImage(userId, file) { // Créer une référence de stockage const storageRef = ref(storage, `users/${userId}/profile/${file.name}`); // Télécharger le fichier const uploadTask = uploadBytesResumable(storageRef, file); return new Promise((resolve, reject) => { uploadTask.on( 'state_changed', (snapshot) => { // Suivre la progression const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log(`Téléchargement : ${progress.toFixed(0)}%`); }, (error) => { // Gérer les erreurs switch (error.code) { case 'storage/unauthorized': reject(new Error('Vous n\'avez pas la permission')); break; case 'storage/canceled': reject(new Error('Téléchargement annulé')); break; default: reject(new Error('Le téléchargement a échoué')); } }, async () => { // Téléchargement terminé const downloadURL = await getDownloadURL(uploadTask.snapshot.ref); console.log('Fichier disponible à :', downloadURL); resolve(downloadURL); } ); });
} // Utilisation
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0]; if (file) { const imageUrl = await uploadProfileImage(auth.currentUser.uid, file); // Enregistrer l'URL dans Firestore await updateDoc(doc(db, 'users', auth.currentUser.uid), { profileImage: imageUrl });
}
import { getDownloadURL } from 'firebase/storage'; async function getProfileImage(userId) { const imageRef = ref(storage, `users/${userId}/profile/avatar.png`); try { const url = await getDownloadURL(imageRef); return url; } catch (error) { if (error.code === 'storage/object-not-found') { return null; // Pas d'image de profil } throw error; }
}
import { getDownloadURL } from 'firebase/storage'; async function getProfileImage(userId) { const imageRef = ref(storage, `users/${userId}/profile/avatar.png`); try { const url = await getDownloadURL(imageRef); return url; } catch (error) { if (error.code === 'storage/object-not-found') { return null; // Pas d'image de profil } throw error; }
}
import { getDownloadURL } from 'firebase/storage'; async function getProfileImage(userId) { const imageRef = ref(storage, `users/${userId}/profile/avatar.png`); try { const url = await getDownloadURL(imageRef); return url; } catch (error) { if (error.code === 'storage/object-not-found') { return null; // Pas d'image de profil } throw error; }
}
import { deleteObject } from 'firebase/storage'; async function deleteProfileImage(userId) { const imageRef = ref(storage, `users/${userId}/profile/avatar.png`); await deleteObject(imageRef); console.log('Image de profil supprimée');
}
import { deleteObject } from 'firebase/storage'; async function deleteProfileImage(userId) { const imageRef = ref(storage, `users/${userId}/profile/avatar.png`); await deleteObject(imageRef); console.log('Image de profil supprimée');
}
import { deleteObject } from 'firebase/storage'; async function deleteProfileImage(userId) { const imageRef = ref(storage, `users/${userId}/profile/avatar.png`); await deleteObject(imageRef); console.log('Image de profil supprimée');
}
POST https://firestore.googleapis.com/v1/projects/{projectId}/databases/(default)/documents
Authorization: Bearer {oauth2_token}
Content-Type: application/json { "fields": { "name": { "stringValue": "John" }, "email": { "stringValue": "[email protected]" }, "age": { "integerValue": 30 } }
}
POST https://firestore.googleapis.com/v1/projects/{projectId}/databases/(default)/documents
Authorization: Bearer {oauth2_token}
Content-Type: application/json { "fields": { "name": { "stringValue": "John" }, "email": { "stringValue": "[email protected]" }, "age": { "integerValue": 30 } }
}
POST https://firestore.googleapis.com/v1/projects/{projectId}/databases/(default)/documents
Authorization: Bearer {oauth2_token}
Content-Type: application/json { "fields": { "name": { "stringValue": "John" }, "email": { "stringValue": "[email protected]" }, "age": { "integerValue": 30 } }
}
POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={api_key}
Content-Type: application/json { "email": "[email protected]", "password": "secret123", "returnSecureToken": true
}
POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={api_key}
Content-Type: application/json { "email": "[email protected]", "password": "secret123", "returnSecureToken": true
}
POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={api_key}
Content-Type: application/json { "email": "[email protected]", "password": "secret123", "returnSecureToken": true
}
# Démarrer l'émulateur
firebase emulators:start # Tester contre Firestore local
# http://localhost:8080
# Démarrer l'émulateur
firebase emulators:start # Tester contre Firestore local
# http://localhost:8080
# Démarrer l'émulateur
firebase emulators:start # Tester contre Firestore local
# http://localhost:8080
// Logique de réessai pour les échecs transitoires
async function firestoreWithRetry(operation, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { return await operation(); } catch (error) { if ( error.code === 'unavailable' || error.code === 'deadline-exceeded' ) { const delay = Math.pow(2, i) * 1000; // Recul exponentiel await new Promise(resolve => setTimeout(resolve, delay)); continue; } throw error; } }
}
// Logique de réessai pour les échecs transitoires
async function firestoreWithRetry(operation, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { return await operation(); } catch (error) { if ( error.code === 'unavailable' || error.code === 'deadline-exceeded' ) { const delay = Math.pow(2, i) * 1000; // Recul exponentiel await new Promise(resolve => setTimeout(resolve, delay)); continue; } throw error; } }
}
// Logique de réessai pour les échecs transitoires
async function firestoreWithRetry(operation, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { return await operation(); } catch (error) { if ( error.code === 'unavailable' || error.code === 'deadline-exceeded' ) { const delay = Math.pow(2, i) * 1000; // Recul exponentiel await new Promise(resolve => setTimeout(resolve, delay)); continue; } throw error; } }
}
// Cette requête nécessite un index composite
const q = query( collection(db, 'posts'), where('category', '==', 'tech'), where('views', '>', 1000), orderBy('views', 'desc')
);
// Cette requête nécessite un index composite
const q = query( collection(db, 'posts'), where('category', '==', 'tech'), where('views', '>', 1000), orderBy('views', 'desc')
);
// Cette requête nécessite un index composite
const q = query( collection(db, 'posts'), where('category', '==', 'tech'), where('views', '>', 1000), orderBy('views', 'desc')
);
import { writeBatch } from 'firebase/firestore'; async function bulkUpdate(userIds, updates) { const batch = writeBatch(db); userIds.forEach(id => { const ref = doc(db, 'users', id); batch.update(ref, updates); }); await batch.commit(); console.log(`Mis à jour ${userIds.length} utilisateurs`);
} // Maximum 500 opérations par lot
import { writeBatch } from 'firebase/firestore'; async function bulkUpdate(userIds, updates) { const batch = writeBatch(db); userIds.forEach(id => { const ref = doc(db, 'users', id); batch.update(ref, updates); }); await batch.commit(); console.log(`Mis à jour ${userIds.length} utilisateurs`);
} // Maximum 500 opérations par lot
import { writeBatch } from 'firebase/firestore'; async function bulkUpdate(userIds, updates) { const batch = writeBatch(db); userIds.forEach(id => { const ref = doc(db, 'users', id); batch.update(ref, updates); }); await batch.commit(); console.log(`Mis à jour ${userIds.length} utilisateurs`);
} // Maximum 500 opérations par lot
// FAUX : Ne jamais faire cela dans le code client
admin.initializeApp({ credential: admin.credential.cert(require('./serviceAccountKey.json'))
}); // CORRECT : Utiliser uniquement dans un environnement serveur
const serviceAccount = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT);
admin.initializeApp({ credential: admin.credential.cert(serviceAccount)
});
// FAUX : Ne jamais faire cela dans le code client
admin.initializeApp({ credential: admin.credential.cert(require('./serviceAccountKey.json'))
}); // CORRECT : Utiliser uniquement dans un environnement serveur
const serviceAccount = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT);
admin.initializeApp({ credential: admin.credential.cert(serviceAccount)
});
// FAUX : Ne jamais faire cela dans le code client
admin.initializeApp({ credential: admin.credential.cert(require('./serviceAccountKey.json'))
}); // CORRECT : Utiliser uniquement dans un environnement serveur
const serviceAccount = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT);
admin.initializeApp({ credential: admin.credential.cert(serviceAccount)
});
// Activer la persistance hors ligne (web)
import { enableMultiTabIndexedDbPersistence } from 'firebase/firestore'; enableMultiTabIndexedDbPersistence(db) .catch((err) => { if (err.code === 'failed-precondition') { // Plusieurs onglets ouverts } else if (err.code === 'unimplemented') { // Le navigateur ne prend pas en charge } }); // Écouter la connectivité
import { onSnapshot } from 'firebase/firestore'; onSnapshot(doc(db, 'status', 'online'), (doc) => { if (!doc.exists()) { console.log('Vous êtes hors ligne'); // Afficher l'interface utilisateur hors ligne }
});
// Activer la persistance hors ligne (web)
import { enableMultiTabIndexedDbPersistence } from 'firebase/firestore'; enableMultiTabIndexedDbPersistence(db) .catch((err) => { if (err.code === 'failed-precondition') { // Plusieurs onglets ouverts } else if (err.code === 'unimplemented') { // Le navigateur ne prend pas en charge } }); // Écouter la connectivité
import { onSnapshot } from 'firebase/firestore'; onSnapshot(doc(db, 'status', 'online'), (doc) => { if (!doc.exists()) { console.log('Vous êtes hors ligne'); // Afficher l'interface utilisateur hors ligne }
});
// Activer la persistance hors ligne (web)
import { enableMultiTabIndexedDbPersistence } from 'firebase/firestore'; enableMultiTabIndexedDbPersistence(db) .catch((err) => { if (err.code === 'failed-precondition') { // Plusieurs onglets ouverts } else if (err.code === 'unimplemented') { // Le navigateur ne prend pas en charge } }); // Écouter la connectivité
import { onSnapshot } from 'firebase/firestore'; onSnapshot(doc(db, 'status', 'online'), (doc) => { if (!doc.exists()) { console.log('Vous êtes hors ligne'); // Afficher l'interface utilisateur hors ligne }
});
// Forcer le rafraîchissement du jeton
const user = auth.currentUser;
if (user) { await user.getIdToken(true); // Forcer le rafraîchissement
}
// Forcer le rafraîchissement du jeton
const user = auth.currentUser;
if (user) { await user.getIdToken(true); // Forcer le rafraîchissement
}
// Forcer le rafraîchissement du jeton
const user = auth.currentUser;
if (user) { await user.getIdToken(true); // Forcer le rafraîchissement
}
// Garder les fonctions "chaudes" avec des pings planifiés
exports.keepWarm = onSchedule('toutes les 60 secondes', async () => { await fetch('https://votre-fonction.cloudfunctions.net/health');
});
// Garder les fonctions "chaudes" avec des pings planifiés
exports.keepWarm = onSchedule('toutes les 60 secondes', async () => { await fetch('https://votre-fonction.cloudfunctions.net/health');
});
// Garder les fonctions "chaudes" avec des pings planifiés
exports.keepWarm = onSchedule('toutes les 60 secondes', async () => { await fetch('https://votre-fonction.cloudfunctions.net/health');
}); - Vous avez besoin d'une synchronisation en temps réel (chat, collaboration, mises à jour en direct)
- Vous souhaitez une architecture sans serveur (pas de gestion d'infrastructure)
- Vous développez des applications mobiles ou web (les SDK gèrent les différences de plateforme)
- Vous avez besoin d'un support hors ligne (les SDK mettent automatiquement les données en cache)
- Vous souhaitez une authentification intégrée (Google, Apple, e-mail, connexion téléphonique) - Vous avez besoin de requêtes relationnelles complexes (utilisez plutôt PostgreSQL)
- Vous avez des exigences strictes en matière de résidence des données (les régions Firebase sont limitées)
- Vous avez besoin de toutes les capacités SQL (des limitations de requêtes Firestore existent)
- Le coût à grande échelle est plus important que la vitesse de développement (l'auto-hébergement est moins cher) - Allez à la Console Firebase - E-mail/Mot de passe : Activez pour une inscription traditionnelle
- Google : Ajoutez votre empreinte de certificat SHA-1 (Android) ou l'ID de bundle (iOS)
- Apple : Obligatoire pour les applications iOS si vous activez une connexion sociale
- Téléphone : Activez pour l'authentification par SMS (nécessite une facturation) - Pas de requêtes OU (utilisez in avec un tableau ou plusieurs requêtes)
- Pas de recherches par caractères génériques (utilisez Algolia ou Meilisearch pour le texte intégral)
- Les requêtes composées nécessitent des index (Firestore crée automatiquement un index sur un seul champ)
- Limite de 30 disjonctions dans les requêtes in - Ouvrez Apidog
- Créez un nouveau projet : "Firebase API"
- Importez la spécification OpenAPI de la documentation Firebase
- Ou ajoutez manuellement des points d'accès : - Créer une requête : "Connexion"
- Définir la méthode : POST
- Ajouter l'e-mail/mot de passe dans le corps
- Enregistrer le jeton de réponse comme variable d'environnement
- Utiliser {{token}} dans les requêtes ultérieures - Vérifiez les règles dans la console Firebase
- Vérifiez que request.auth.uid correspond à l'utilisateur attendu
- Testez les règles avec le Playground de règles - Authentification : Connexion par e-mail, Google, Apple avec des jetons JWT
- Firestore : Base de données NoSQL avec des écouteurs en temps réel et des règles de sécurité
- Cloud Functions : Backend sans serveur déclenché par des événements ou HTTP
- Stockage : Téléchargement de fichiers avec distribution CDN