Aller au contenu

Firebase pour une Web App en Prod et sans gluten

Explorez les avantages de Firebase pour le déploiement, la sécurité et la gestion des coûts, offrant ainsi une solution simple, efficace et accessible.

Une Web App en prod avec Firebase

Avec la digitalisation croissante dans l'industrie de la restauration, la gestion numérique des menus et des informations sur les allergènes est devenue essentielle.

En tant qu'ingénieur full stack spécialisé dans le front-end avec Angular, j'ai entrepris de développer une application web pour gérer et afficher les informations sur les allergènes des plats d'un restaurant. Face aux contraintes d'hébergement, de stockage des données sécurisé, de coûts et de scalabilité, le choix de la solution technique était crucial.

Dans cet article, j'explique pourquoi j'ai choisi Firebase pour déployer en production ma Web App qrplat.com et ceci sans prise de tête et sans gluten sans symptômes digestifs !

qrplat.com

Postulat

L'objectif était de trouver une solution technique simple qui puisse répondre aux besoins suivants :

  • Hébergement : un service fiable et performant.
  • Stockage des données : une base de données sécurisée et facilement accessible.
  • Coûts : une solution économique, surtout au démarrage.
  • Scalabilité : une capacité à évoluer facilement en cas de succès.
  • Simplicité de mise en œuvre : facile à utiliser pour un développeur peu familier avec le cloud et le déploiement.

Pourquoi Firebase ?

Après avoir exploré plusieurs options, j'ai choisi Firebase pour plusieurs raisons :

  • Déploiement et hébergement avec Firebase Hosting

Firebase fournit une CLI (interface de ligne de commande) qui permet de déployer une application web en une seule ligne de commande : firebase deploy. L’application est immédiatement disponible via une URL 😍.

  • Stockage des données avec Cloud Firestore

Cloud Firestore est une base de données NoSQL flexible qui permet de stocker et de synchroniser les données en temps réel. Cela s'est avéré parfait pour gérer les informations variées sur les allergènes pour chaque restaurant. Les requêtes sont rapides et puissantes, et les règles de sécurité permettent de protéger les données sensibles.

La data est accessible via une interface (la console Firebase) avec la possibilité d’utiliser des filtres ainsi qu'un générateur de requêtes pour afficher, interroger, ajouter, modifier et supprimer des données.

console Firebase
Générateur de requêtes
  • Gestion des coûts

Firebase propose un niveau gratuit (Spark Plan) qui inclut un hébergement gratuit pour des sites à faible trafic, ainsi qu'un quota gratuit pour Cloud Firestore et Firebase Storage. Cela m'a permis de démarrer le projet sans coûts initiaux importants. En cas de succès et d'augmentation du trafic, le passage au plan payant (Blaze Plan) est flexible et basé sur l'utilisation réelle.

  • Scalabilité

La scalabilité est intégrée avec Firebase. L'infrastructure est conçue pour gérer des millions d'utilisateurs (suite à la publication de cet article 🤩), ce qui garantit une expérience utilisateur fluide même en cas de forte affluence.

  • Simplicité de mise en œuvre

Firebase est conçu pour être accessible même aux développeurs qui ne sont pas experts en cloud et en déploiement. Les services sont bien documentés, et l'intégration avec Angular via AngularFire simplifie grandement le développement.

Installation et implémentation avec Angular

En tant que développeur Angular, l'intégration de Firebase est facilitée grâce au CLI et à AngularFire, une bibliothèque officielle qui simplifie l'utilisation de Firebase avec Angular.

L’installation et la configuration se limitent à quelques lignes. La recette se compose de 4 ingrédients principaux :

  • un compte Google
  • la CLI Firebase
  • la librairie AngularFire
  • un projet Angular (16 ou 17 pour une version d'AngularFire 7.x.x)

Etape 1 : créer et configurer un projet Firebase

Connectez-vous à Firebase : https://console.firebase.google.com

Ajoutez un nouveau projet, puis ajoutez une application Web Firebase au projet. À la fin du process, copiez collez la configuration générée dans votre projet Angular (environments/environment.ts).

Depuis la console Web, activez la connexion Google pour l'authentification Firebase et Cloud Firestore pour la base de données.

Etape 2 : se connecter à Firebase depuis notre projet Angular

Installez Firebase CLI à l'aide de cette commande dans un terminal npm install -g firebase-tools

Utilisez votre compte Google pour vous connecter : firebase login
Connectez votre projet Angular à l’application Web Firebase firebase init

N’activez pas l'émulateur dans l’initialisation, cette partie ne sera pas traitée dans cet article.

Etape 3 : configurer le projet Angular

Ajoutez AngularFire avec cette commande : ng add @angular/fire
Configurez les modules Firebase dans app.module.ts

import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
import { environment } from '../../environments/environment';
import { getAuth, provideAuth } from '@angular/fire/auth';
import { getFirestore, provideFirestore } from '@angular/fire/firestore';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule,
    NgbModule,
    // Initialisation de Firebase
    provideFirebaseApp(() => initializeApp(environment.firebase)),
    // Initialisation du module d'authentification
    provideAuth(() => getAuth()),
    // Initialisation du module firestore
    provideFirestore(() => getFirestore()),
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Et voilà tout est prêt ! Voyons maintenant comment appeler firebase depuis la couche service d'Angular.

Un CRUD pour la collection resto

QRPlat va utiliser une collection contenant une liste de restaurant (document).
Voici le modèle côté typescript :

import {Timestamp} from 'firebase/firestore';

export type Resto = {
  id?: string; //id généré par firestore lors de l'insertion
  reference: string; //ex: 'dessertissime'-> https://qrplat.com/dessertissime
  creationDate: Timestamp;
  name: string;
  email: string;
}

La structure est identique côté Firestore et s'appelle un document.
Et maintenant le service qui appelle concrètement firebase :

import {
  addDoc, // pour ajouter un document
  collection,
  collectionData,
  deleteDoc, // pour supprimer un document
  doc, // pour attacher un document
  docData, // pour lire un document
  Firestore,
  limit, // pour limiter le nombre de résultat
  orderBy, // pour ordonner le résultat
  query, // pour créer la requête
  updateDoc,
  where,
} from '@angular/fire/firestore';
// ...
@Injectable({
  providedIn: 'root',
})
export class RestoService {
  const FIREBASE_STORE_RESTO = 'resto'; // resto est le nom de la collection
  firestore: Firestore = inject(Firestore);
  restoRef = collection(this.firestore, this.FIREBASE_STORE_RESTO);
  // ...
}

Crud pour créer un document

createResto(resto: Resto) {
  return addDoc(this.restoRef, resto);
}

cRud pour lire un ou plusieurs documents

getRestoById(resto: Resto): Observable<Resto> {
  const restoDocRef = doc(
    this.firestore,
    `${this.FIREBASE_STORE_RESTO}/${resto.id}`
  );
  return docData(restoDocRef, { idField: 'id' }) as Observable<Resto>;
}
loadRestos(): Observable<Array<Resto>> {
  const lastRestosQuery = query(
    collection(this.firestore, this.FIREBASE_STORE_RESTO),
    orderBy('creationDate', 'desc'),
    limit(200),
  );
  return collectionData(lastRestosQuery, {idField: 'id'}) as Observable<
    Array<Resto>
  >;
}

crUd pour mettre à jour un document

updateResto(resto: Resto) {
  const restoDocRef = doc(
    this.firestore,
    `${this.FIREBASE_STORE_RESTO}/${resto.id}`
  );
  return updateDoc(restoDocRef, resto);
}

cruD pour supprimer un document

deleteResto(resto: Resto) {
  const restoDocRef = doc(
    this.firestore,
    `${this.FIREBASE_STORE_RESTO}/${resto.id}`
  );
  return deleteDoc(restoDocRef);
}

Facile n'est ce pas ? En plus la data synchronisée en temps réel 😮

Si dans mon contrôleur un observable écoute la function loadRestos(), il sera notifié dès qu'un nouveau document resto sera inséré.

export class MyComponent implements OnInit {
  restoService: RestoService = inject(RestoService);
  restos$: Observable<Array<Resto>>; //mis à jour en temps réel
  
  ngOnInit(): void {
    this.restos$ = this.restoService.loadRestos();
  }
  // ...
}

C'est parfait pour modifier un allergène en temps réel en cas de changement de recette ou simplement en cas d'oubli.

Module d'authentification intégré dans Firebase

Firebase propose un module d'authentification intégré qui simplifie la gestion des utilisateurs. Il prend en charge plusieurs méthodes de connexion, y compris par email/mot de passe, les réseaux sociaux (Google, Facebook, Twitter), et même des fournisseurs d'identité tiers comme GitHub. Ce module offre également des fonctionnalités de gestion des utilisateurs, telles que la réinitialisation de mot de passe et la vérification de l'email.

Pour l'utiliser, vous comprenez mieux l'ajout de cette ligne plus haut provideAuth(() => getAuth())

Voyons de plus près l'utilisation de ce module dans un service dédié.

import {
  Auth,
  createUserWithEmailAndPassword,
  GoogleAuthProvider,
  signInWithEmailAndPassword,
  signInWithPopup,
  signOut,
  user
} from "@angular/fire/auth";
import {UserCredential} from "@firebase/auth";
// ...
export class AuthenticationService {
  private auth: Auth = inject(Auth);
  private provider = new GoogleAuthProvider();
  user$ = user(this.auth);

  async login(): Promise<UserCredential> {
    return await signInWithPopup(this.auth, this.provider);
  }

  async logout(): Promise<void> {
    return signOut(this.auth);
  }

  async signUp(email: string, password: string): Promise<UserCredential> {
    return createUserWithEmailAndPassword(this.auth, email, password);
  }

  async signIn(email: string, password: string): Promise<UserCredential> {
    return signInWithEmailAndPassword(this.auth, email, password);
  }

  getCurrentUser() {
    return this.auth.currentUser;
  }

  isUserSignedIn() {
    return !!this.auth.currentUser;
  }
}

J'entends d'ici vos "wouaahhh", "incroyable !!!!", mais ce n'est pas fini 😸

Pour QRPlat j'avais besoin de recevoir un mail à chaque fois qu'un nouveau restaurant était créé. Firebase est de type serverless, c'est à dire qu'il n'y a pas de service backend à proprement parler. En revanche, il y a des Cloud Functions ...

Les Cloud Functions

Firebase Functions est un service de cloud computing qui permet d'exécuter du code backend en réponse à des événements déclenchés par Firebase et d'autres services Google Cloud. Voici quelques points clés sur Firebase Functions :

  • Fonctions sans serveur : pas besoin de gérer les serveurs, le code s'exécute en réponse à des événements.
  • Événements déclencheurs : réagissez à des événements Firebase (authentification, Firestore, etc.), HTTP, et bien plus.
  • Scalabilité automatique : les fonctions s'adaptent automatiquement à la charge.

Voici un exemple de fonction Cloud qui se déclenche lorsqu'un resto est inséré dans Firestore :

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

exports.onDocumentCreate = functions.firestore
  .document('resto/{docId}')
  .onCreate((snap, context) => {
    const newValue = snap.data();
    console.log('Document créé avec les données:', newValue);
    
    // Envoi de mail via une API
    // ...
  });

Règles de sécurité pour Firestore

Voici un dernier point important sur la gestion de l'accès aux données de Firestore avec le code suivant:

service cloud.firestore {
  match /databases/{database}/documents {
    match /resto/{docId} {
      allow read, write: if request.auth != null;
    }
  }
}

Nous lisons ici que la collection resto est accessible par tout le monde en lecture (via AngularFire) mais que l'écriture est limitée aux utilisateurs connectés uniquement. Nous n'approfondirons pas cette partie, car cela nécessiterait un article dédié.

Conclusion

Le contrat est rempli avec Firebase 🥳 ! J'ai pu développer ma Web App en quelques jours seulement sans me soucier des problématiques de déploiement, de sécurité et tout cela gratuitement. En plus, c'était aussi simple que de suivre une recette sans gluten!

J'ai souhaité écrire cet article en vous présentant les fonctionnalités essentielles de Firebase, mais beaucoup d'autres modules sont disponibles. Par exemple, vous pouvez connecter votre projet à Google Analytics, gérer les paiements en ligne avec Stripe, utiliser le stockage pour héberger des images, consulter les logs, et bien plus encore.

Si vous cherchez une solution efficace et facile pour votre projet, Firebase est votre meilleur allié. Pour plus de détails ou si vous envisagez de l'utiliser, n'hésitez pas à me contacter !

Dernier