Déplacer Angular HttpClient vers le backend Fetch à l’aide de withFetch()
Découvrez pourquoi HttpClient d’Angular passe de XMLHttpRequest à fetch, comment fetch() permet le SSR aux frontières et la diffusion en flux, ainsi que ce que cela implique pour vos intercepteurs.
Sources: Stop Using XMLHttpRequest: Why Angular 22 Shifted to the Native Fetch API
Pendant la majeure partie de son existence, HttpClient d’Angular a représenté une couche RxJS conviviale placée au-dessus de XMLHttpRequest, une API navigateur antérieure à presque tous les autres éléments de la plateforme web moderne. Ce choix devient de plus en plus un inconvénient lorsque votre application s’affiche sur le serveur, fonctionne sur des environnements d’exécution edge ou consomme des réponses en flux continu. Ce guide explique pourquoi un backend basé sur fetch est important, comment le passer en utilisant une seule option de fournisseur, ainsi que ce qui change ou non pour vos intercepteurs existants.
Pourquoi XMLHttpRequest est devenu un inconvénient
XHR remonte à 1999, date à laquelle Microsoft l’a créé pour alimenter Outlook Web Access. Sa conception trahit son âge : il est à état, basé sur des événements et difficile à mettre en œuvre, et l’écosystème JavaScript plus large s’en éloigne depuis près d’une décennie. La plupart des bibliothèques et environnements de exécution ont depuis longtemps opté pour fetch(), basé sur les promesses. Angular a conservé XHR en arrière-plan principalement en raison de son fort engagement en faveur de la compatibilité vers l’arrière.
Cet équilibre a changé lorsque Angular a adopté la détection de modifications sans zone et le rendu du côté serveur. Dans ce contexte, XHR n’est plus simplement du code ancien que personne ne veut modifier ; il limite activement l’endroit et la manière dont une application Angular peut fonctionner. Un backend basé sur fetch() permet au framework de moderniser sa pile réseau, de gérer correctement les données en flux et de fonctionner sur des plateformes edge.
Lors de la rédaction de ce texte, les dernières versions d’Angular privilégient fetch comme moteur par défaut pour HttpClient. Les paramètres par défaut et les fonctionnalités obsolètes changent d’une version majeure à l’autre, il est donc conseillé de consulter les notes de version correspondant à celle que vous utilisez plutôt que d’imaginer quel backend est actif.
Les environnements Edge ne fournissent pas XMLHttpRequest
L’hébergement des backends évolue progressivement, passant de processus Node.js complets dans des conteneurs Docker à des isolats V8 légers tels que Cloudflare Workers, Deno et Fastly Compute. Si vous souhaitez en savoir plus sur ce modèle d’environnement, consultez notre comparaison des isolats Edge, WebAssembly et environnements Node.js.
Ces plateformes sont conçues autour d’API modernes conformes aux normes web, et XMLHttpRequest n’en fait pas partie. Une build traditionnelle Angular SSR déployée sur une telle plateforme échoue dès que HttpClient effectue sa première requête API lors du rendu de la page, car le mécanisme de transport nécessaire n’existe tout simplement pas.
fetch(), en revanche, est disponible dans les navigateurs, Node.js, Deno ainsi que dans les environnements d’exécution edge. Une fois que HttpClient l’utilise, la même application SSR devient portable : vous pouvez la rendre dans Cloudflare Workers ou Deno Deploy et effectuer des appels API depuis des lieux géographiquement plus proches de vos utilisateurs.
Changer le backend avec withFetch()
Cette modification correspond à une changement de configuration, et non à un refactoring. Les composants et services continuent d’appeler this.http.get() ainsi que les fonctions associées exactement comme avant ; seule la configuration du fournisseur dans le fichier de configuration de l’application change.
Le fragment ci-dessous enregistre HttpClient à l’aide de provideHttpClient() et transmet deux fonctionnalités. withFetch() sélectionne le transport basé sur Fetch, tandis que withInterceptors() enregistre un intercepteur d’authentification fonctionnel défini plus loin.
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
import { authInterceptor } from './core/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
// Injects HttpClient using the modern Fetch API backend
provideHttpClient(
withFetch(),
withInterceptors([authInterceptor])
)
]
};
En interne, withFetch() indique à Angular d’utiliser FetchBackend au lieu de HttpXhrBackend comme composant chargé d’exécuter réellement la requête réseau. Tout ce qui se trouve au-dessus de cette couche reste inchangé.
Une limitation à connaître avant d’activer cette fonctionnalité : le backend de fetch ne rapporte pas les événements de progression du téléchargement, car fetch() ne dispose pas d’équivalent à la fonction de suivi du téléchargement d’XHR. Si votre application affiche une barre de progression pour les téléchargements de fichiers, testez explicitement ce flux.
Réponses en streaming sans stockage complet en mémoire
XHR a été conçu pour des réponses complètes et autonomes. Il gère mal les données volumineuses ou continues : le téléchargement d’un fichier de 50 MB ou la lecture d’une réponse fragmentée entraîne généralement l’accumulation d’un grand buffer en mémoire avant que JavaScript ne puisse s’en servir, ce qui provoque une pression sur la mémoire et un fonctionnement lent de l’interface.
fetch() a été conçu autour de ReadableStream, ce qui permet de consommer le contenu par tranches au fur et à mesure qu’il arrive depuis le réseau. Grâce au backend fetch, Angular peut traiter les données reçues dès leur arrivée plutôt que d’attendre la réception de tout le chargement.
Cela est particulièrement important pour les interfaces de type IA. Une interface de chat qui lit des Server-Sent Events ou des sorties par tranches d’un LLM a besoin que les tokens apparaissent au fur et à mesure de leur génération, et un transport capable du streaming constitue la base pour y parvenir sans accumuler tout cela en mémoire du navigateur.
Soyez précis quant à ce que vous obtenez. Une requête http.get() ordinaire émet toujours la réponse finale une fois le corps du message reçu ; l’accès progressif s’effectue via des événements de progression (reportProgress avec observe: 'events'), et pour les flux de tokens à longue durée, de nombreuses équipes recourent encore à EventSource ou à une appel direct de fetch(). Vérifiez le comportement souhaité en consultant la documentation de votre version d’Angular.
Que deviennent vos intercepteurs
Lorsque le moteur réseau change, la première question posée par un ingénieur expérimenté concerne les intermédiaires : les intercepteurs d’authentification et de journalisation continueront-ils de fonctionner ?
Avec des intercepteurs fonctionnels, la réponse est oui. L’interception s’appuie sur les abstractions HttpRequest et HttpResponse d’Angular, qui se situent au-dessus du mécanisme de transport. Les intercepteurs transforment l’objet de demande d’Angular, et ce n’est qu’après que toute la chaîne ait été exécutée que la demande finale parvient à FetchBackend, lequel la convertit en une appel fetch(). L’intercepteur ne touche jamais à la demande native.
L’exemple ci-dessous est indépendant du backend. Il lit un JWT, clone la demande immuable en y ajoutant une en-tête Authorization, puis transmet cette copie à next, qui la renvoie à l’intercepteur suivant et finalement au backend.
import { HttpInterceptorFn } from '@angular/common/http';
// This interceptor works perfectly with both XHR and Fetch backends
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = localStorage.getItem('jwt');
// Clone and mutate the Angular abstraction, not the native request
const secureReq = req.clone({
setHeaders: { Authorization: `Bearer ${token}` }
});
// Hand off to the next interceptor, and ultimately, the Fetch backend
return next(secureReq);
};
Deux précautions s’appliquent à ce fragment en particulier et non au backend fetch. Premièrement, localStorage n’existe pas lors du rendu côté serveur, donc dans les environnements SSR ou edge, vous devez protéger l’appel ou injecter le jeton différemment. Deuxièmement, stocker des jetons dans localStorage les expose à toute vulnérabilité XSS, et le code envoie Bearer null lorsqu’aucun jeton n’est présent ; le code de production doit gérer ces deux cas.
Les intercepteurs basés sur des classes fonctionnent toujours
Les intercepteurs plus anciens, écrits sous forme de classes implémentant HttpInterceptor, continuent de fonctionner avec le backend fetch. Cependant, ils semblent de plus en plus décalés par rapport au reste du framework. La méthode conforme aux conventions actuelles d’Angular consiste à combiner withFetch() avec des intercepteurs fonctionnels enregistrés via withInterceptors() ; il est donc judicieux de migrer ceux basés sur des classes lorsque l’on y touche.
Points clés
- Passer de
XHRà fetch pourHttpClientreprésente une amélioration discrète mais significative : une seule option de fournisseur, sans modification du code d’appel. - Ajoutez
withFetch()àprovideHttpClient()si votre version n’utilise pas déjà fetch par défaut. - C’est grâce à fetch que Angular SSR peut fonctionner sur Cloudflare Workers, Deno et d’autres environnements de bord similaires qui ne disposent pas de
XMLHttpRequest.
ReadableStream, ce qui facilite le traitement de charges volumineuses et de réponses fragmentées comme les sorties des LLM, mais il convient de vérifier comment HttpClient affiche les données partielles avant de créer une interface utilisateur en streaming à partir d’elles.HttpInterceptorFn) continuent de fonctionner sans modification ; testez l’avancement des téléchargements ainsi que tous les chemins de code réservés au SSR après ce changement.