Le cycle requête réponse - rocambille/start-express-react GitHub Wiki
Le cycle requête-réponse est au cœur de toute application web. Dans StartER, il est pris en charge par une architecture unifiée combinant Express (API) et React (interface), le tout géré par un serveur unique.
Lorsqu'un utilisateur interagit avec StartER, chaque action suit un enchaînement précis : de la requête du navigateur à la réponse rendue par le serveur.
- Le navigateur envoie une requête HTTP au serveur unique (GET, POST, PUT, DELETE...)
- Le serveur Express analyse et traite cette requête à travers une série de middlewares
- Le contenu est généré selon le chemin correspondant : données JSON pour une API ou page HTML rendue côté serveur
- Le serveur envoie une réponse HTTP au navigateur (contenant les données formatées en JSON ou la page HTML rendue)
- Le client React s'hydrate (pour les pages web) et devient interactif
┌──────────────┐ HTTP Request (1) ┌─────────────┐ Data Request ┌─────────────┐
│ │───────────────────> │ │───────────────────> │ │
│ Browser │ │ Express │ │ Database │
│ │ <───────────────────│ Server (2) │ <──────────────────│ │
└──────────────┘ HTTP Response (4) └─────────────┘ Data (3a) └─────────────┘
▲ ▲ │
│ │ │
│ HTML (3b) │ ▼ Page Request
┌──────────────┐ ┌─────────────┐
│ │ │ │
│ React Client │ <────Hydration───── │ React SSR │
│ (Browser) │ (5) │ (Server) │
└──────────────┘ └─────────────┘
Ce schéma illustre la circulation des données entre le navigateur, le serveur Express, la base de données et le moteur React SSR.
Le cycle peut varier selon le type de requête (API ou page) et l'état d'authentification de l'utilisateur.
Le fichier server.ts est le point d'entrée de l'application. Il configure un serveur Express unique, chargé à la fois de :
- servir les routes API
- rendre les pages React via le Server-Side Rendering (SSR).
const app = express();
// Express API routes
app.use((await import("./src/express/routes")).default);
// Middleware "catch-all" for SSR
app.use(/(.*)/, async (req, res, next) => {
// SSR logic...
});Les requêtes adressées à l'API (préfixées par /api) sont traitées par Express via un système de routage modulaire.
Chaque module est isolé dans src/express/modules, ce qui rend l'architecture facile à étendre.
Les routes sont organisées par modules dans le dossier src/express/modules et utilisées dans src/express/routes.ts :
/* ************************************************************************ */
import authRoutes from "./modules/auth/authRoutes";
router.use(authRoutes);
/* ************************************************************************ */
import itemRoutes from "./modules/item/itemRoutes";
router.use(itemRoutes);
/* ************************************************************************ */
import userRoutes from "./modules/user/userRoutes";
router.use(userRoutes);
/* ************************************************************************ */Chaque module définit ses propres routes. Par exemple, pour le module src/express/modules/item dans itemRoutes.ts :
const BASE_PATH = "/api/items";
const ITEM_PATH = "/api/items/:itemId";
router.get(BASE_PATH, itemActions.browse);
router.get(ITEM_PATH, itemActions.read);
router.post(BASE_PATH, itemValidator.validate, itemActions.add);
router.route(ITEM_PATH)
.all(checkAccess)
.put(itemValidator.validate, itemActions.edit)
.delete(itemActions.destroy);Avant d'arriver à leur gestionnaire final, les requêtes traversent une chaine de middlewares :
- Limitation de débit (rate limiting) par IP
- Parsing JSON (
express.json()) - Vérification d'authentification via JWT
- Validation des données avec
Zod - ...
Les gestionnaires de requêtes (actions) sont responsables du traitement final et de la génération des réponses :
const add: RequestHandler = async (req, res) => {
const insertId = await itemRepository.create(req.body);
res.status(201).json({ insertId });
};
const read: RequestHandler = (req, res) => {
res.json(req.item);
};Les réponses à une requête d'API peuvent être :
- Des objets JSON (pour les requêtes GET et POST)
- Des codes de statut HTTP (pour les requêtes PUT et DELETE)
- Des codes d'erreur (400, 401, 403, 404...)
Pour toutes les autres requêtes (celles qui ne visent pas /api), StartER utilise React en mode Server-Side Rendering (SSR) pour générer le HTML initial des pages.
Le processus se déroule en 4 étapes dans entry-server.tsx :
-
Obtenir un contexte de routage
const context = await query( new Request(`${req.protocol}://${req.get("host")}${req.originalUrl}`), );
-
Créer un routeur statique pour le SSR
const router = createStaticRouter(dataRoutes, context);
-
Faire le rendu avec
StaticRouterProviderconst { pipe } = renderToPipeableStream( <StrictMode> <StaticRouterProvider router={router} context={context} /> </StrictMode>, );
-
Envoyer la réponse HTML au client
res.status(200).set("Content-Type", "text/html; charset=utf-8"); res.write(htmlStart); // ... pipe(transformStream);
Ces quatre étapes aboutissent à une page HTML complète envoyée au navigateur, prête à être "réhydratée" côté client.
Voir le code complet pour les détails :
Une fois le HTML initial chargé, le JavaScript client prend le relais : il réactive les composants React déjà présents dans la page et rend l'interface interactive.
hydrateRoot(
root,
<StrictMode>
<RouterProvider router={router} />
</StrictMode>,
);Cette hydratation utilise les données pré-chargées pendant le SSR pour éviter de refaire les requêtes initiales.
Voir le code complet pour les détails :
Pour approfondir les aspects techniques du framework, consultez les pages suivantes :