-
Notifications
You must be signed in to change notification settings - Fork 7
Le cycle requête réponse fr FR
Résumé : 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 pour être utilisées dans src/express/routes.ts. Cela pourrait ressembler à :
/* ************************************************************************ */
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);
/* ************************************************************************ */Note
Le fichier src/express/routes.ts fournit la fonction importAndUse qui combine import() dynamique et router.use(). Cela permet de garder le fichier de routes léger et de faciliter l'ajout ou le retrait de modules.
// Utilitaire pour importer dynamiquement un module et l'enregistrer sur le router
const importAndUse = async (path: string) =>
router.use((await import(path)).default);
await importAndUse("./modules/auth/authRoutes");
await importAndUse("./modules/item/itemRoutes");
await importAndUse("./modules/user/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 action finale, les requêtes traversent une chaîne 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 actions sont responsables du traitement final et de la génération des réponses :
const add: RequestHandler = (req, res) => {
const insertId = 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.
Note
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.
Note
Voir le code complet pour les détails.
- Gardez les actions légères : déléguez la logique métier aux services ou aux repositories.
-
Comprendre le modèle I/O : StartER utilise l'API synchrone de SQLite pour les appels à la base de données (pas de
async/awaitnécessaire pour les requêtes). Utilisez les opérations asynchrones uniquement pour les ressources réellement asynchrones (par exemple, les appels vers des API externes ou l'envoi d'e-mails).
Co-création IA
Bien démarrer
Explications
Guides
Référence
Aller plus loin