mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-04-06 21:41:42 +02:00
167 lines
7.7 KiB
Markdown
167 lines
7.7 KiB
Markdown
# Backend — Patterns : Next.js
|
|
|
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/patterns/README.md` pour l'index complet.
|
|
|
|
---
|
|
|
|
<a id="pattern-nextjs-runtime-only-logique-pure-testable"></a>
|
|
## Pattern : Next.js runtime-only — orchestration en bord et logique pure testable
|
|
|
|
- Objectif : préserver la testabilité unitaire et la lisibilité du code serveur Next.js en limitant les dépendances runtime-only aux couches d'orchestration.
|
|
- Contexte : applications Next.js avec Server Actions, route handlers, modules email/auth et logique métier testée côté Node.
|
|
- Quand l'utiliser : dès qu'un flux serveur mélange APIs Next.js runtime-only (`cookies()`, `headers()`, `redirect()`, `server-only`) et logique métier réutilisable.
|
|
- Quand l'éviter : petits modules purement runtime sans logique métier notable, ou fonctions triviales sans intérêt à être testées séparément.
|
|
- Avantage :
|
|
- garde la logique métier importable dans un runner Node standard
|
|
- évite que `server-only` contamine des modules purs
|
|
- facilite les tests unitaires sans mocks lourds du runtime Next.js
|
|
- clarifie la responsabilité des Server Actions et handlers serveur
|
|
- Limites / vigilance :
|
|
- demande une discipline de découpage
|
|
- peut introduire une indirection inutile si la logique extraite est réellement triviale
|
|
- les frontières d'injection doivent rester simples pour éviter un excès d'abstraction
|
|
- Validé le : 19-03-2026
|
|
- Contexte technique : Next.js / Server Actions / Node test runner / modules backend injectables
|
|
|
|
### Implémentation (exemple minimal)
|
|
|
|
```txt
|
|
- réserver `import "server-only"` aux fichiers qui utilisent réellement des APIs runtime Next.js
|
|
- garder la Server Action, route handler ou module email comme couche d'orchestration fine
|
|
- extraire la logique métier pure dans une fonction ou un service sans dépendance à `cookies()`, `headers()`, `redirect()` ou `server-only`
|
|
- injecter explicitement les dépendances utiles (client DB, token, callback de redirect, logger, etc.)
|
|
- tester unitairement le module pur dans le runner Node ; tester l'orchestrateur plus légèrement
|
|
```
|
|
|
|
### Checklist
|
|
|
|
- `server-only` absent des modules de logique pure
|
|
- APIs Next.js runtime-only limitées aux couches d'entrée
|
|
- Logique métier principale testable sans runtime Next.js
|
|
- Dépendances injectées explicitement quand utile
|
|
- Server Action ou handler fin et lisible
|
|
|
|
---
|
|
|
|
<a id="pattern-nextjs-server-only-isolation"></a>
|
|
## Pattern : Next.js server-only & Server Actions — règles d'isolation
|
|
|
|
- Objectif : permettre les tests unitaires Node tout en gardant les contraintes runtime Next.js là où elles sont nécessaires.
|
|
- Contexte : monorepo Next.js App Router avec logique métier testée en Node runner natif.
|
|
- Quand l'utiliser : dès qu'un module mixe logique pure et dépendances runtime Next.js.
|
|
- Quand l'éviter : modules purement UI côté client.
|
|
- Avantage :
|
|
- logique pure testable sans friction (runner Node natif)
|
|
- Server Action fine et lisible — orchestration uniquement
|
|
- `server-only` explicite et intentionnel, pas par habitude
|
|
- Limites / vigilance :
|
|
- ne pas mettre `server-only` dans les repositories purs — casse les tests Node hors Next.js
|
|
- Validé le : 16-03-2026
|
|
- Contexte technique : Next.js App Router / Node.js test runner
|
|
|
|
### Règles
|
|
|
|
```txt
|
|
- `server-only` uniquement sur les modules qui appellent des APIs Next.js runtime
|
|
(cookies(), headers(), redirect()) — pas sur les repositories ni la logique pure
|
|
- Logique pure extraite dans un module injectable sans `server-only` :
|
|
deleteSession({ prismaClient, sessionToken })
|
|
→ testable avec le runner Node sans friction
|
|
- Server Action = orchestration mince, elle appelle les modules purs injectés
|
|
et gère les dépendances Next.js runtime uniquement
|
|
- Logique de validation / sanitisation (safeHttpUrl, etc.) → module utilitaire séparé,
|
|
sans import nodemailer / server-only
|
|
```
|
|
|
|
### Checklist
|
|
|
|
- [ ] `server-only` absent des repositories et modules de logique pure
|
|
- [ ] Server Action ≤ 10 lignes, délègue au module pur injectable
|
|
- [ ] Modules purs couverts par des tests `.spec.ts` Node sans config spéciale
|
|
- [ ] La logique pure ne dépend pas du runtime pour être exécutée
|
|
|
|
---
|
|
|
|
<a id="pattern-utilitaires-purs-module-partage"></a>
|
|
## Pattern : Utilitaires purs — extraire dans un module sans `server-only`
|
|
|
|
- Objectif : permettre aux repositories et aux tests d'importer la même implémentation des utilitaires purs sans friction.
|
|
- Contexte : fonctions pures (slugify, formatters, validators) utilisées par des repositories qui ont `server-only`.
|
|
- Quand l'utiliser : dès qu'une fonction pure est utilisée dans un repository ET dans des tests.
|
|
- Risque si ignoré : logique dupliquée dans les tests qui diverge silencieusement de l'implémentation réelle.
|
|
- Validé le : 21-03-2026
|
|
- Contexte technique : Node.js / Next.js — app-template-resto
|
|
|
|
### Implémentation
|
|
|
|
```
|
|
src/server/menuAdmin/
|
|
allergensRepository.ts ← import { slugify } from "./slugify"
|
|
slugify.ts ← export function slugify() {} // pas de "server-only"
|
|
|
|
tests/
|
|
allergens-admin.test.ts ← import { slugify } from "../src/server/menuAdmin/slugify.ts"
|
|
```
|
|
|
|
---
|
|
|
|
<a id="pattern-reutiliser-champ-existant-v1"></a>
|
|
## Pattern : Réutiliser un champ existant plutôt que créer un modèle dédié en V1
|
|
|
|
- Objectif : éviter la sur-ingénierie en V1 en réutilisant un champ existant quand le besoin est simple.
|
|
- Contexte : early-stage, besoin de stocker une configuration simple (URL, flag, valeur unique).
|
|
- Quand l'utiliser : quand la donnée a le même cycle de vie qu'un modèle existant et ne nécessite pas de relations.
|
|
- Quand l'éviter : si la configuration a son propre cycle de vie, des cardinalités multiples, ou des relations distinctes.
|
|
- Avantage : zéro migration supplémentaire, zéro scope creep
|
|
- Validé le : 17-03-2026
|
|
- Contexte technique : Prisma / Node.js — app-template-resto
|
|
|
|
### Règle
|
|
|
|
```txt
|
|
- Avant de créer un modèle ReservationConfig, vérifier si PublicHomeProfile.reservationUrl suffit
|
|
- Un champ optionnel dans le modèle le plus proche est suffisant en V1
|
|
- Ne créer un modèle dédié que si : cycle de vie distinct, relations, ou cardinalités multiples
|
|
```
|
|
|
|
---
|
|
|
|
<a id="pattern-validation-url-externe"></a>
|
|
## Pattern : Valider le protocole d'une URL externe avant de la passer à un lien public
|
|
|
|
- Objectif : prévenir les injections `javascript:` et URLs malformées dans les `<a href>` ou `<img src>` publics.
|
|
- Contexte : toute URL venant d'une config tenant, DB ou saisie utilisateur, rendue dans le HTML.
|
|
- Quand l'utiliser : systématiquement sur tout champ URL libre stocké en DB et rendu côté HTML.
|
|
- Risque si ignoré : injection `javascript:`, URL malformée, potentiel XSS.
|
|
- Validé le : 17-03-2026
|
|
- Contexte technique : Node.js / Next.js — app-template-resto
|
|
|
|
### Implémentation
|
|
|
|
```typescript
|
|
function isSafeUrl(url: string): boolean {
|
|
try {
|
|
const { protocol } = new URL(url);
|
|
return protocol === "https:" || protocol === "http:";
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Validation complète en service/repository
|
|
if (mediaUrl) {
|
|
try { new URL(mediaUrl); } catch { throw new HttpError("URL invalide.", { status: 400 }); }
|
|
if (!mediaUrl.startsWith("https://") && !mediaUrl.startsWith("http://"))
|
|
throw new HttpError("URL doit commencer par https://.", { status: 400 });
|
|
if (mediaUrl.length > 500)
|
|
throw new HttpError("URL trop longue.", { status: 400 });
|
|
}
|
|
// Retourner null si invalide — le composant gère l'absence d'URL
|
|
```
|
|
|
|
### Checklist
|
|
|
|
- [ ] Validation format (`new URL()`) + protocole + longueur max
|
|
- [ ] Retourner `null` si invalide, jamais passer la string brute
|
|
- [ ] Composant UI reçoit `string | null`, jamais une string non vérifiée
|