Tools: Comment Utiliser l'API Firebase: Guide d'Intégration Complet (2026)

Tools: Comment Utiliser l'API Firebase: Guide d'Intégration Complet (2026)

Qu'est-ce que l'API Firebase et pourquoi est-elle importante ?

Services Firebase principaux

Quand Firebase est pertinent

L'architecture de l'API Firebase

Authentification Firebase : Configuration complète

Étape 1 : Créer un projet Firebase

Étape 2 : Enregistrer votre application

Étape 3 : Activer les méthodes d'authentification

Étape 4 : Implémenter le flux d'authentification

Étape 5 : Protéger les routes avec l'état d'authentification

Erreurs d'authentification courantes

Base de données Firestore : Opérations et requêtes

Structure des données

Initialiser Firestore

Créer des documents

Lire des documents

Mettre à jour des documents

Supprimer des documents

Écouteurs en temps réel

Règles de sécurité Firestore

Limitations des requêtes

Cloud Functions : Logique backend sans serveur

Configuration

Fonctions HTTP (points d'accès API)

Déclencheurs de base de données

Fonctions planifiées (tâches Cron)

Configuration de l'environnement

Cloud Storage : Téléchargement et gestion de fichiers

Configuration des règles de stockage

Télécharger des fichiers (Client)

Télécharger des fichiers

Supprimer des fichiers

Tester les API Firebase avec Apidog

Importer l'API REST Firebase

Tester le flux d'authentification

Déboguer les règles de sécurité

Bonnes pratiques de production

1. Implémenter une gestion d'erreurs appropriée

2. Optimiser les performances des requêtes

3. Opérations par lots

4. Surveiller les coûts

5. Sécuriser les comptes de service

6. Gérer les scénarios hors ligne

Problèmes et solutions courants de l'API Firebase

Problème 1 : Erreurs d'autorisation refusée

Problème 2 : Expiration du jeton

Problème 3 : Latence de démarrage à froid

Problème 4 : La requête renvoie des résultats vides

Cas d'utilisation réels

Application Fintech : Mises à jour de transactions en temps réel

E-commerce : Synchronisation des stocks

SaaS : Authentification multi-locataires

Conclusion

Firebase est-il gratuit ?

Puis-je utiliser Firebase avec des bases de données existantes ?

Comment migrer de Firebase vers une autre plateforme ?

Firebase prend-il en charge GraphQL ?

Puis-je utiliser Firebase sur site ?

Comment gérer les téléchargements de fichiers de plus de 100 Mo ?

Que se passe-t-il si je dépasse les limites de requêtes Firestore ?

Firebase est-il conforme au RGPD ? Vous développez une application. Les utilisateurs doivent se connecter. Les données doivent se synchroniser en temps réel. Les fichiers nécessitent un stockage. Vous pourriez déployer des serveurs, configurer des bases de données et gérer l'infrastructure pendant des semaines. Ou vous pourriez utiliser Firebase. Essayez Apidog dès aujourd'hui Firebase alimente plus de 1,5 million d'applications, dont The New York Times, Duolingo et Alibaba. Les développeurs le choisissent car il élimine la complexité du backend. Vous vous concentrez sur les fonctionnalités, pas sur la maintenance des serveurs. Mais l'API Firebase a ses particularités. Les flux d'authentification déroutent les débutants. Les règles de base de données piègent les développeurs expérimentés. Les fonctions Cloud semblent magiques jusqu'à ce que vous compreniez les déclencheurs. J'ai intégré Firebase dans des applications de production servant des millions d'utilisateurs. J'ai commis toutes les erreurs possibles : exposé des clés de compte de service, écrit des requêtes inefficaces, déployé des fonctions défectueuses. Ce guide condense ces leçons. Vous apprendrez l'authentification, les opérations de base de données, les fonctions Cloud et le stockage. Vous verrez du code fonctionnel, pas seulement de la théorie. Vous éviterez les pièges qui causent des problèmes en production. 💡Tester les API Firebase devient plus facile avec des outils clients API appropriés. Apidog vous permet d'organiser les points d'accès, de tester les flux d'authentification et de partager des collections avec votre équipe. Nous vous montrerons où il s'intègre naturellement dans le flux de travail. Firebase n'est pas une seule API. C'est une suite de services backend accessibles via des SDK unifiés et des points d'accès REST. Firebase résout bien des problèmes spécifiques : Utilisez Firebase lorsque : Évitez Firebase lorsque : Firebase utilise une approche hybride : Les SDK clients abstraient la couche HTTP. En arrière-plan, chaque opération se traduit par des appels d'API REST avec authentification JWT. L'authentification est votre première intégration Firebase. Si cela échoue, tout le reste échoue. Cliquez sur "Ajouter un projet" et saisissez le nom du projet (pas d'espaces) Activez Google Analytics (facultatif mais recommandé) Cliquez sur "Créer le projet" Attendez 30 secondes pour le provisionnement. Vous verrez le tableau de bord du projet. Pour les applications web : Pour les applications iOS : Téléchargez GoogleService-Info.plist et ajoutez-le au projet Xcode. Assurez-vous que "Target Membership" inclut votre application. Pour les applications Android : Téléchargez google-services.json et placez-le dans le répertoire app/. Ajoutez à build.gradle : Dans la console Firebase > Authentification > Méthode de connexion : Inscription par e-mail/mot de passe : Connexion par e-mail/mot de passe : Connexion Google (Web) : Erreur 1 : Ne pas gérer le rafraîchissement des jetons Le SDK Firebase rafraîchit automatiquement les jetons. Mais si vous mettez en cache les jetons côté serveur, ils expirent après 1 heure. Vérifiez toujours les jetons à chaque requête ou implémentez une logique de rafraîchissement. Erreur 2 : Exposer les identifiants d'administrateur dans le code client N'utilisez jamais les clés de compte de service dans les applications clientes. Les comptes de service contournent les règles de sécurité. Utilisez-les uniquement dans des environnements serveur de confiance. Erreur 3 : Oublier la vérification de l'e-mail Firestore est la base de données NoSQL de Firebase. Les documents sont organisés en collections. Les requêtes s'adaptent automatiquement. Sans règles appropriées, n'importe qui peut lire vos données. Définissez les règles dans la console Firebase > Firestore > Règles : Firestore a des contraintes : Solution de contournement pour les requêtes OU : Les fonctions Cloud exécutent du code backend sans gérer de serveurs. Elles se déclenchent lors de modifications de base de données, de requêtes HTTP ou d'événements planifiés. Appel depuis le client : Stockez les téléchargements d'utilisateurs, les images et les fichiers avec une distribution CDN automatique. Firebase fournit des API REST pour tous les services. Les tester directement aide à déboguer les problèmes et à comprendre les requêtes sous-jacentes. Point d'accès REST Firestore : Point d'accès d'authentification : Utilisez la suite d'émulateurs Firebase pour les tests locaux : Ajoutez des index composites pour les requêtes multi-champs : Firestore vous invite à créer l'index avec un lien direct lorsque vous exécutez cette requête. Tarification Firebase : Définissez des alertes budgétaires dans la console Google Cloud. Symptôme : Erreur : 7 PERMISSION_DENIED Cause : Les règles de sécurité bloquent l'opération Symptôme : Erreur : Jeton d'ID expiré Symptôme : Les fonctions Cloud prennent 2 à 5 secondes lors du premier appel Symptôme : La requête devrait renvoyer des données mais renvoie un tableau vide Cause : Index manquant ou ordre des champs incorrect Correction : Vérifiez la console Firestore > Index pour les index composites requis. Une startup de paiement a utilisé Firebase Firestore pour créer des notifications de transactions en temps réel. Lorsqu'un paiement est traité, les fonctions Cloud déclenchent des mises à jour sur tous les tableaux de bord d'administration connectés en moins de 200 ms. Résultat : une réduction de 40 % des tickets de support concernant les transactions "en attente". Un détaillant en ligne synchronise son inventaire sur le web, iOS et Android à l'aide des écouteurs Firestore. Lorsque le stock change, tous les clients sont mis à jour automatiquement. La persistance hors ligne garantit que les employés de l'entrepôt peuvent scanner des articles sans connectivité, avec une synchronisation automatique une fois reconnectés. Une plateforme B2B utilise Firebase Auth avec des revendications personnalisées pour l'accès basé sur les rôles. Les utilisateurs administrateurs obtiennent des autorisations élevées via des fonctions Cloud qui valident les configurations de locataires Firestore. Une seule base de code sert plus de 500 organisations avec des données isolées. L'intégration de l'API Firebase implique quatre services principaux : Vous avez appris les flux d'authentification, les opérations de base de données, le déploiement de fonctions et la gestion de fichiers. Vous avez vu des modèles de production : gestion des erreurs, traitement par lots, support hors ligne et sécurité. Oui, Firebase propose un niveau gratuit généreux (plan Spark) incluant 5 Go de stockage, 50K lectures Firestore/jour, 2M invocations de fonctions Cloud et 10K utilisateurs Auth/mois. Les plans payants (Blaze) utilisent une tarification à l'usage. Oui. Utilisez les extensions Firebase pour synchroniser avec PostgreSQL, MySQL ou MongoDB. Ou appelez des API externes depuis les fonctions Cloud pour vous intégrer à des systèmes existants. Exportez les données à l'aide des fonctions d'exportation Firestore ou de la CLI Firebase. Pour les grands ensembles de données, utilisez le pipeline d'exportation Dataflow. La complexité de la migration dépend de la structure de vos données. Pas nativement. Utilisez des solutions tierces comme firestore-graphql ou construisez une couche GraphQL avec Cloud Functions et Apollo Server. Non. Firebase est exclusivement sur Google Cloud. Pour des alternatives auto-hébergées, envisagez Appwrite, Supabase ou Nhost. Utilisez les téléchargements reprenables avec découpage (chunking). Le SDK Firebase gère cela automatiquement. Pour les fichiers très volumineux, utilisez directement Google Cloud Storage avec des URL signées. Les requêtes échouent avec l'erreur FAILED_PRECONDITION. Ajoutez les index requis ou restructurez les requêtes. Firestore fournit des liens directs pour créer les index manquants dans le message d'erreur. Oui, Firebase offre un traitement des données conforme au RGPD. Activez la résidence des données dans des régions spécifiques, implémentez l'exportation/suppression des données utilisateur et signez l'avenant relatif au traitement des données de Google. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Code Block

Copy

┌─────────────────────────────────────────────────────────┐ │ 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