Mettre en place un serveur local avec HMR et proxy inversé

Un serveur de développement local réduit considérablement le temps séparant une modification de code de son résultat visible dans le navigateur. Avec le rechargement à chaud, ou HMR pour Hot Module Replacement, seuls les modules concernés sont actualisés. L’état de l’interface est souvent conservé, ce qui rend le cycle de développement plus fluide qu’un rechargement complet de la page.

Le proxy inversé complète ce dispositif en plaçant un serveur intermédiaire entre le navigateur et les différents services de l’application. Les requêtes vers /api, les fichiers statiques, une base distante ou un service d’authentification peuvent ainsi être dirigés vers leur destination sans exposer cette organisation au client.

Cette architecture est particulièrement utile pour une application React, Vue, Svelte ou Angular associée à une API Node.js. Elle évite les problèmes de CORS, centralise les variables d’environnement et reproduit plus fidèlement la circulation des requêtes prévue en production.

Solution HMR intégré Proxy inversé Cas d’usage
Vite Oui, très rapide Oui, natif SPA modernes et TypeScript
Webpack Dev Server Oui, configurable Oui Projets Webpack existants
Serveur Node.js personnalisé À développer Oui, avec middleware Besoins spécifiques
Nginx ou Caddy local Non Oui Simulation d’une infrastructure de production

Comprendre le rôle de chaque composant

Le serveur de développement sert généralement les fichiers du frontend, surveille le système de fichiers et transforme les modules à la demande. Vite s’appuie sur les modules ES natifs en développement, tandis que Webpack construit un graphe de dépendances plus traditionnel. Dans les deux cas, un canal WebSocket transmet au navigateur les changements détectés.

Lorsqu’un fichier CSS est enregistré, le navigateur peut remplacer directement la feuille de style. Pour un module JavaScript, le bundler tente d’actualiser uniquement la partie modifiée. Si cette opération n’est pas sûre, par exemple après une modification de la structure d’un composant, un rechargement complet est déclenché.

Le proxy inversé possède une mission différente. Il reçoit une requête comme http://localhost:5173/api/users, retire éventuellement le préfixe /api, puis la transmet à http://localhost:3000/users. Le frontend conserve ainsi une URL relative stable, quel que soit l’emplacement réel du backend.

Configurer Vite pour un projet frontend

Avec Vite, la configuration se trouve habituellement dans vite.config.ts. Une base fonctionnelle peut ressembler à ceci :

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  server: {
    port: 5173,
    strictPort: true,
    proxy: {
      "/api": {
        target: "http://localhost:3000",
        changeOrigin: true,
        secure: false
      }
    }
  }
});

Le frontend peut alors appeler l’API avec fetch("/api/users"), sans inscrire le port du backend dans chaque composant. changeOrigin adapte l’en-tête Host à la cible distante, tandis que secure: false peut être nécessaire avec un certificat TLS autosigné en environnement local.

Le HMR est activé par défaut dans un projet Vite. Si l’application tourne dans Docker, une machine virtuelle ou un volume partagé, les événements de fichiers peuvent être mal transmis. L’option usePolling: true dans server.watch force alors une vérification périodique, au prix d’une consommation supérieure en ressources.

Pour les projets qui manipulent des modèles, des données ou des services d’intelligence artificielle, cette séparation facilite aussi les essais locaux. Les développeurs peuvent consulter des guides ML tout en gardant les appels sensibles derrière une route serveur, au lieu d’exposer des clés dans le bundle JavaScript.

Relier le frontend, l’API et les services externes

Un proxy de développement ne sert pas uniquement à éviter CORS. Il peut concentrer plusieurs destinations : /api vers une API Node.js, /auth vers un fournisseur d’identité et /uploads vers un service de stockage. Cette organisation reproduit un routage par chemins proche de celui d’un ingress Kubernetes ou d’un reverse proxy Nginx.

Le backend doit toutefois connaître les paramètres propres à son environnement. Une variable comme DATABASE_URL reste côté serveur, tandis que les variables préfixées par VITE_ sont généralement incorporées dans le code livré au navigateur. Il faut donc éviter d’y placer un secret, une clé privée ou un mot de passe de base de données.

Lorsqu’une API interroge une base relationnelle ou documentaire, le proxy ne remplace pas une stratégie de connexion correcte. Les ressources consacrées aux bases de données aident à distinguer les responsabilités entre client, serveur applicatif, pool de connexions et système de stockage.

Pour un service externe, deux choix sont possibles : le proxy peut transmettre directement la requête, ou le backend peut agir comme façade. La seconde option offre un meilleur contrôle des autorisations, de la validation des entrées, de la journalisation et de la limitation de débit.

Gérer HTTPS, les WebSockets et les chemins

Le passage en HTTPS local devient utile dès que l’application utilise des cookies sécurisés, des API nécessitant un contexte sécurisé ou des fonctionnalités comme les notifications. Des outils tels que mkcert permettent de générer un certificat approuvé par la machine de développement, sans désactiver toutes les protections du navigateur.

Le proxy doit également laisser passer les connexions persistantes. HMR utilise souvent WebSocket, tout comme certaines API temps réel. Une configuration incomplète peut afficher l’application normalement tout en empêchant la mise à jour à chaud. Avec un proxy externe, les en-têtes Upgrade et Connection doivent être transmis correctement.

Les chemins méritent une attention particulière. Une règle /api peut conserver le préfixe ou le supprimer selon les attentes du serveur cible. Un décalage entre /api/users et /users produit souvent une erreur 404 difficile à relier au proxy. Il est préférable de documenter explicitement cette convention et de la couvrir par un test simple.

Un serveur local peut aussi servir de passerelle vers une ressource publique utilisée comme exemple ou contenu de test, comme un magazine en ligne. Cette pratique doit rester limitée aux environnements appropriés : les données externes peuvent changer, répondre lentement ou imposer des règles d’accès différentes.

Préparer une configuration fiable au quotidien

Les fichiers de configuration doivent distinguer les environnements. Une configuration locale peut cibler localhost, une autre un conteneur Docker nommé api, et la production un nom DNS interne. Les fichiers .env.local ne doivent pas être versionnés lorsqu’ils contiennent des informations confidentielles.

Les journaux du proxy sont essentiels pour diagnostiquer une requête. Afficher la méthode HTTP, le chemin, la cible et le code de réponse permet de différencier un problème de routage d’une erreur applicative. En complément, l’onglet Network des outils de développement révèle les réponses CORS, les déconnexions WebSocket et les redirections inattendues.

Le cache mérite aussi une stratégie distincte. En développement, des en-têtes trop agressifs peuvent masquer une modification et donner l’impression que le HMR ne fonctionne pas. Pour les applications progressives, les techniques décrites dans cet article sur les workers de service montrent pourquoi le cache du navigateur doit être contrôlé séparément du cache du serveur de développement.

Un service worker déjà enregistré peut intercepter les requêtes avant même qu’elles atteignent le proxy. Il faut parfois le désinscrire, vider les caches ou utiliser une fenêtre privée pendant le diagnostic. La mise en cache côté client avec Cache API devient ensuite un choix explicite, testé dans des conditions proches de celles des utilisateurs.

Tester le flux complet avant la livraison

Un environnement local efficace doit vérifier plusieurs scénarios : modification CSS, mise à jour d’un composant, erreur de compilation, redémarrage de l’API, expiration d’un cookie et réponse lente d’un service distant. Ces cas révèlent rapidement les différences entre un simple serveur de fichiers et une véritable chaîne de développement.

L’application doit être lancée avec des scripts reproductibles, par exemple une commande pour le frontend, une autre pour l’API et une commande commune avec concurrently ou Docker Compose. Les ports, les dépendances et les variables nécessaires sont alors visibles par toute l’équipe, ce qui réduit les configurations individuelles difficiles à maintenir.

Avant de déployer, il faut vérifier que le proxy de développement n’est pas utilisé comme mécanisme de sécurité. L’authentification, la validation, les permissions et la protection contre les attaques doivent être assurées par les services concernés. En production, un reverse proxy dédié peut reprendre le routage, la terminaison TLS, la compression et les limites de requêtes.

Adoptez une configuration courte, documentée et versionnée, puis testez régulièrement HMR, les WebSockets et chaque route proxifiée. Un serveur local bien conçu transforme les modifications quotidiennes en vérifications rapides, tout en préparant une transition plus prévisible vers Docker, un cloud ou une infrastructure d’intégration continue.