J5 — Les contrats
Vérifié contre le produit le .
L’objectif du jour : ajouter un champ et le voir arriver jusqu’à l’écran, en sachant à chaque étape ce qui est écrit à la main et ce qui est engendré.
| Durée réaliste | une demi-journée, exercice compris |
| Prérequis | J3 — la carte, et le réflexe du ^build |
Deux contrats, pas un
Section intitulée « Deux contrats, pas un »Le dépôt en porte deux, et on les confond volontiers :
packages/schemas— des schémas Zod écrits à la main, partagés par l’API, le web et le solveur. C’est le contrat de validation : mêmes règles des deux côtés du réseau, et des fonctions pures partagées plutôt que dupliquées.packages/api-client— un client TypeScript entièrement engendré depuis l’OpenAPI que l’API émet. C’est le contrat de transport : les chemins, les paramètres, les formes de réponse.
Le premier se modifie. Le second se régénère. Rien ne s’écrit à la main sous
packages/api-client/src/generated/.
La chaîne que personne ne devine
Section intitulée « La chaîne que personne ne devine »Elle est déclarée dans turbo.json, et c’est la seule partie du dépôt qui a besoin d’être lue
avant d’être comprise :
api#openapi:emit → @beemyschool/api-client#generate → @beemyschool/api-client#buildEn clair :
api#openapi:emitdémarre l’application NestJS sans écouter de port, construit le document OpenAPI et l’écrit dansapps/api/openapi.json. Il tourne enNODE_ENV=test, ce qui saute les migrations au démarrage : aucune base n’est nécessaire pour engendrer la spécification.generatepasse ce fichier àopenapi-typescript, qui écritpackages/api-client/src/generated/schema.ts.buildcompile le paquet, queapps/webconsomme.
Le typecheck du client dépend lui aussi de generate — sinon il vérifierait un fichier absent
ou périmé.
Ce qu’il faut relancer après un changement de contrat : la chaîne entière, ce que fait
bun run build à la racine. Turbo en respecte l’ordre et ne rejoue que ce qui a bougé.
L’exercice : un champ, de la base à l’écran
Section intitulée « L’exercice : un champ, de la base à l’écran »Prenez un champ optionnel sur une ressource existante — c’est le meilleur exercice du dépôt, parce qu’il traverse tout sans rien risquer.
packages/db/src/schema.ts— ajoutez la colonne, puisbun run --filter @beemyschool/db db:generateet relisez le SQL produit.packages/schemas— ajoutez le champ au schéma Zod de la ressource. Optionnel, pour ne rien casser chez les appelants existants.apps/api— le module concerné : le champ traverse le contrôleur, le service et la réponse.- La chaîne —
bun run buildà la racine. Vérifiez que le champ apparaît bien dansapps/api/openapi.json, puis danspackages/api-client/src/generated/schema.ts. apps/web— affichez-le. Le client typé vous guide : si le champ n’existe pas dans les types, c’est que l’étape 4 n’a pas été rejouée.- Les tests — un cas côté API, et
bun run typecheckà la racine pour la traversée complète.
Si les six étapes passent, vous savez livrer une fonctionnalité de bout en bout. C’est aussi la première contribution que propose J7.
Le piège du jour
Section intitulée « Le piège du jour »Ce qui doit exister à la fin de la journée
Section intitulée « Ce qui doit exister à la fin de la journée »- Vous savez distinguer le contrat de validation (
schemas) du contrat de transport (api-client) - Vous savez réciter la chaîne
openapi:emit → generate → buildet dire ce que produit chaque étape - Vous savez que la génération de l’OpenAPI ne demande aucune base
- Votre champ d’exercice remonte jusqu’à l’écran
- Vous savez où regarder —
openapi.json, puisgenerated/schema.ts— quand un type manque
J6 — Le solveur : le seul module du dépôt qui ne soit pas en TypeScript, et le seul dont le contrat est tenu par deux implémentations à la fois.