Dans cet article nous allons voir comment éviter de faire des appels HTTP inutiles en mettant en place un cache côté Angular.
Cet article est le prolongement de la vidéo YouTube "Mise en cache des requêtes HTTP en Angular" disponible ci-dessous.
Pourquoi mettre en cache ?
Mettre en cache des appels HTTP permet d'optimiser les performances de votre application Angular en évitant de requêter inutilement votre Backend.
Imaginez-vous en train de requêter votre serveur à chaque fois qu'une donnée est affichée sur votre application même si cette donnée ne change jamais. Dans ce cas-là, requêter une seule fois votre serveur est suffisant.
Vous avez envie de mettre en cache les requêtes qui renvoient tout le temps les mêmes données, qui ne changent que très rarement ou dont le changement peut être prévu à l'avance.
Requêtes HTTP inutiles dans Angular
Dans Angular nous utilisons le service HttpClient pour réaliser des requêtes HTTP. Nous définissons des méthodes qui renvoient un Observable sur lesquelles nous devons nous abonner pour déclencher la requête HTTP.
export class MyService {
constructor(private httpClient: HttpClient) { }
getStatuses(): Observable {
return this.httpClient.get(`http://localhost:3000/statuses`)
}
}
Si nous appellons plusieurs fois subscribe() sur la méthode getStatuses() alors il y aura une requête HTTP pour chaque subscribe().
this.service.getStatuses().subscribe() // Envoi une première requête HTTP
this.service.getStatuses().subscribe() // Envoi une deuxième requête HTTP
this.service.getStatuses().subscribe() // Envoi une troisième requête HTTP
Si nous regardons l'onglet Network, nous avons autant de requêtes que de subscribe(). Ce sont des requêtes inutiles qu'il faut mettre en cache.
Onglet Network avec 3 requêtes HTTP
Mettre en cache une méthode renvoyant un Observable
Bien que assez simple je vous déconseille de gérer le cache côté Angular avec du code fait maison car le code va être verbeux et difficile à maintenir surtout dans des équipes avec plusieurs développeurs.
Installer le package ts-cacheable
Pour mettre facilement notre appel HTTP en cache nous allons installer le package ts-cacheable. C'est le package que j'utilise sur tous les projets sur lesquels j'interviens pour simplifier le code des services.
npm install --save ts-cacheable
Utiliser l'annotation @Cacheable
Pour mettre en cache le résultat de notre méthode il suffit de rajouter l'annotation @Cacheable() de ts-cacheable sur notre méthode.
import { Cacheable } from "ts-cacheable"
@Cacheable()
getStatuses(): Observable {
return this.httpClient.get(`http://localhost:3000/statuses`)
}
À partir de maintenant, seul le premier subscribe() va déclencher la requête HTTP. Ensuite les prochains utiliseront la donnée mise en cache de la première requête.
this.service.getStatuses().subscribe() // Envoi une requête HTTP
this.service.getStatuses().subscribe() // Utilise le cache
this.service.getStatuses().subscribe() // Utilise le cache
Une seule requête est exécutée grâce à l'annotation @Cacheable()
Persister le cache avec le LocalStorage ou SessionStorage
Si vous souhaitez que votre cache reste après un rafraichissement de la page vous pouvez utiliser le LocalStorage ou le SessionStorage pour votre cache. Pour cela il suffit de setter la propriété storageStrategy avec la stratégie de votre choix.
import { Cacheable, LocalStorageStrategy } from "ts-cacheable"
@Cacheable({ storageStrategy: LocalStorageStrategy })
getStatuses(): Observable {
return this.httpClient.get(`http://localhost:3000/statuses`)
}
Durée de vie du cache
Nous pouvons aussi définir combien de temps doit durer le cache, indépendamment de la stratégie de stockage, avec la propriété maxAge qui doit être indiquée en millisecondes.
@Cacheable({ maxAge: 60 * 60 * 1000 }) // Cache qui dure 1 heure
getStatuses(): Observable {
return this.httpClient.get(`${this.baseUrl}/statuses`)
}
Cela est très pratique en combinaison avec le LocalStorage pour ne pas avoir un cache qui dure indéfiniment.
Détruire le cache manuellement
Enfin nous pouvons manuellement détruire un cache, cela peut être pratique si vous avez ajouté un élément à une liste mise en cache et que vous voulez rafraichir cette liste.
Pour cela nous assignons un Subject à la propriété cacheBusterObserver.
const cacheBuster$ = new Subject();
@Injectable({ providedIn: "root" })
export class MyService {
constructor(private httpClient: HttpClient) { }
@Cacheable({ cacheBusterObserver: cacheBuster$ })
getStatuses(): Observable {
return this.httpClient.get(`http://localhost:3000/statuses`)
}
}
this.taskService.getStatuses().subscribe() // Envoi une première requête HTTP
cacheBuster$.next() // On détruit le cache
this.taskService.getStatuses().subscribe() // Envoi une deuxième requête HTTP
Configuration globale
Nous pouvons aussi définir une configuration globale de nos caches avec l'objet GlobalCacheConfig.
import { GlobalCacheConfig } from 'ts-cacheable';
import { LocalStorageStrategy } from 'ts-cacheable';
GlobalCacheConfig.storageStrategy = LocalStorageStrategy;
Conclusion
Nous venons de voir comment mettre en cache très facilement une méthode renvoyant un Observable avec l'annotation @Cacheable(). Cette mise en cache nous permet d'éviter énormément d'appels HTTP inutiles et donc d'améliorer la performance de notre application.
Nous avons vu que selon notre besoin nous pouvons :
Aller plus loin
Le package ts-cacheable fournit plein d'autres options pour configurer vos caches donc n'hésitez pas à aller voir la documentation sur la page du projet.
Pour améliorer l'expérience utilisateur lorsque celui-ci attend la réponse d'une requête HTTP, je vous invites à aller voir cet article sur les Loading Spinners.
Abonnez vous pour ne pas rater les nouveaux articles !
© Gaëtan Rouziès - Tous Droits Réservés