WakaStart
Bonnes pratiques

Bonnes pratiques de déploiement

Règles concrètes pour qu'un service applicatif tourne correctement sur Wakastart : .env.example, Dockerfile, migrations Prisma, runtime config, S3, OAuth.

Version v1.09 min de lecture

Bonnes pratiques de déploiement

Ces règles sont issues des incidents rencontrés sur des services réels. Elles couvrent tout ce qui peut casser silencieusement un déploiement : config manquante, image Docker mal construite, migrations DB oubliées, runtime config Next.js mal alignée, intégration OAuth/Keycloak bancale.


1. Variables d'environnement

Chaque fichier .env.example constitue un contrat de configuration. Il doit être exhaustif, auto-documenté et maintenu à jour à chaque évolution du projet.

1.1 Règles obligatoires pour chaque variable

IcôneAttributExigence
📝Exemple de valeurFournir une valeur d'exemple réelle et fonctionnelle. Pas de placeholder vide type xxx ou changeme.
📌Description contextuelleExpliquer en une phrase pourquoi cette variable est nécessaire au fonctionnement de l'application.
🔁Doublon inter-servicesSi la même valeur est attendue dans un autre microservice (ex: front ET back), documenter le doublon, expliquer pourquoi il existe, et préciser que toute modification doit être répercutée sur tous les services concernés.
✅ / ⚠️Obligatoire ou optionnelleIndiquer clairement si la variable est requise ou optionnelle, et décrire l'impact de son omission (comportement dégradé, erreur fatale, fonctionnalité désactivée…).
🌐Type URLSi la valeur est une URL, préciser si elle doit être publique (accessible depuis le navigateur) ou privée (accessible uniquement depuis le réseau interne / pod K8s).
🧪Dev uniquementSi la variable n'est utilisée qu'en dev, l'indiquer explicitement et expliquer pourquoi elle n'est pas nécessaire en prod.
🔐SecretSi la lecture de cette valeur par une personne non autorisée constitue un risque (fuite de données, accès dangereux — ex: DATABASE_URL, clés API, tokens), la marquer comme SECRET.
🔑Impact chiffrement / autorisationSi la variable impacte un chiffrement ou un système d'autorisation, documenter : (1) les services devant partager la même valeur, (2) les risques de modification (ex: perte définitive d'accès aux données chiffrées), (3) le format attendu et la commande de génération.

1.2 Exemple de documentation

env
# URL de l'API backend consommée par le frontend. # TYPE: URL publique (accessible depuis le navigateur de l'utilisateur). # OBLIGATOIRE — sans cette variable, toutes les requêtes API échouent. # DOUBLON: Cette variable correspond à BACKEND_URL côté backend. # La modifier ici implique de mettre à jour BACKEND_URL dans le service backend. NEXT_PUBLIC_API_URL=https://api.exemple.com # Clé de chiffrement des tokens de session. # TYPE: SECRET — ne jamais committer en clair ni modifier sans plan de migration. # ATTENTION: Modifier cette valeur invalide tous les tokens actifs. # FORMAT: 32 bytes hex — générer avec: openssl rand -hex 32 ENCRYPTION_KEY=<générer avec openssl rand -hex 32>

1.3 NEXT_PUBLIC_* vs variables runtime (Next.js)

Les variables NEXT_PUBLIC_* sont compilées au moment du build par Next.js et inlinées dans le bundle JS. Elles ne peuvent pas être modifiées à l'exécution du conteneur.

Si une URL doit pouvoir être configurée sans rebuild (ex: URL de Keycloak, URL d'API), elle doit passer par le mécanisme window.__RUNTIME_CONFIG__ injecté par docker-entrypoint.sh (voir section 4).

Documenter explicitement dans le .env.example de chaque service :

  • quelles variables sont compilées au build (NEXT_PUBLIC_*)
  • lesquelles sont injectées à l'exécution via window.__RUNTIME_CONFIG__
  • en cas de doublon entre les deux mécanismes, laquelle a la priorité et pourquoi

⚠️ Ne jamais committer un fichier .env réel dans le dépôt Git. Seul le .env.example (sans valeurs secrètes réelles) est versionné.


2. Dockerfile et migrations Prisma

En l'absence d'outillage CI/CD avancé, toute application nécessitant une base de données doit garantir l'application automatique des migrations Prisma au démarrage du conteneur.

2.1 Entrypoint obligatoire pour les applications avec Prisma

Le Dockerfile de toute application backend utilisant Prisma doit déléguer le démarrage à un script entrypoint.sh. Ce script applique les migrations avant de démarrer le processus Node.js principal.

Structure type d'un entrypoint.sh :

sh
#!/bin/sh set -e echo '[entrypoint] Running Prisma migrations...' npx prisma migrate deploy echo '[entrypoint] Starting application...' exec node dist/main.js

Configuration Dockerfile associée :

dockerfile
FROM node:20-alpine WORKDIR /app COPY . . RUN npm install --omit=dev RUN npx prisma generate RUN npm run build COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"]

2.2 Pièges à éviter

  • prisma doit figurer dans les dependencies (et non devDependencies) pour être disponible lors de l'exécution du conteneur de production.
  • 🚨 Ne jamais utiliser pnpm prune --prod sans avoir vérifié que prisma, prisma generate et tous les binaires natifs nécessaires (bcrypt, multer…) sont dans les dependencies de production.

3. TypeScript et build NestJS / Node.js

Les erreurs de build TypeScript sont une cause fréquente de déploiements silencieusement cassés.

RègleDétail
rootDir obligatoire dans tsconfig.build.jsonToujours déclarer rootDir: "./src" pour garantir que la sortie compilée est dans dist/main.js et non dist/src/main.js. Sans rootDir, le chemin de sortie dépend du module résolu et casse le démarrage du conteneur.
Éviter les imports hors rootDirNe jamais importer depuis ../package.json ou tout chemin situé hors du rootDir défini. Ces imports provoquent des erreurs tsc et ne sont pas accessibles dans l'image Docker de production.
Scripts postinstall avec pnpm v10+pnpm v10 bloque les scripts postinstall par défaut. Configurer explicitement pnpm.onlyBuiltDependencies dans package.json pour autoriser la génération des binaires Prisma, bcrypt et autres dépendances natives.
Dépendances natives en productionmulter, bcrypt et tous les packages requis à l'exécution doivent être dans dependencies et non devDependencies, même s'ils sont utilisés via un framework comme NestJS.
prisma.config.ts sans dotenvNe pas importer dotenv/config dans prisma.config.ts. dotenv n'est pas disponible dans l'image de production et les variables d'environnement sont injectées par le système d'orchestration (K8s).

4. Runtime config du frontend (Next.js)

Les applications Next.js déployées en conteneur ont deux sources de configuration : les variables NEXT_PUBLIC_* (compilées au build) et les variables injectées à l'exécution via window.__RUNTIME_CONFIG__. Il faut impérativement aligner les noms de variables entre docker-entrypoint.sh et le déploiement Kubernetes.

Règles pour la runtime config

  • L'entrypoint.sh du frontend doit lire les variables telles qu'elles sont définies dans le déploiement K8s (NEXT_PUBLIC_API_URL, KEYCLOAK_PUBLIC_URL, etc.), et non des alias inventés localement (API_URL, KEYCLOAK_URL…).
  • Toute URL susceptible de changer selon l'environnement sans rebuild de l'image (ex: URL Keycloak) doit être injectée via window.__RUNTIME_CONFIG__ et non lue via process.env.NEXT_PUBLIC_*.
  • Documenter dans le .env.example de chaque service quelles variables sont compilées au build (NEXT_PUBLIC_*) et lesquelles sont injectées à l'exécution.
  • En cas de doublon entre une variable NEXT_PUBLIC_* et une variable runtime, documenter explicitement laquelle a la priorité et pourquoi.

⚠️ Un entrypoint.sh qui lit des variables inexistantes laisse window.__RUNTIME_CONFIG__ avec les valeurs par défaut hardcodées (localhost, preprod…), ce qui est indétectable sans logs.


5. Intégration S3 et Object Storage

Les adaptateurs S3 doivent gérer correctement les URLs d'endpoint pour éviter des erreurs de connectivité silencieuses.

Règles pour les endpoints S3

  • Ne jamais préfixer mécaniquement https:// sur une variable d'endpoint : si la variable contient déjà le protocole, la double-préfixation produit une URL invalide (https://https://…) non détectable par les linters.
  • L'adaptateur doit détecter si l'endpoint contient déjà un protocole, parser l'URL pour en déduire le port (443 pour https, 80 pour http), et ne pas forcer le port 9000 par défaut (qui est le port MinIO local, non le port OVH Object Storage).
  • Documenter dans le .env.example si la variable MINIO_ENDPOINT / S3_ENDPOINT doit ou non inclure le protocole, le port et le chemin.
  • Préférer les variables classiques S3 du type : AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, etc.

6. Authentification et flux OAuth / Keycloak

Voir aussi Authentication (Phase intégration) pour le flux OAuth général. Les règles ci-dessous sont des pièges concrets rencontrés sur des services en production.

6.1 redirect_uri

Le redirect_uri envoyé dans la requête d'autorisation doit être strictement identique à celui utilisé lors de l'échange de code côté serveur. Toute divergence provoque un rejet Keycloak.

Le serveur doit utiliser le redirect_uri fourni par le client lors du callback, et non une valeur hardcodée.

6.2 Clés sessionStorage

Toutes les clés sessionStorage utilisées dans le flux OAuth (realm, client_id, state, code_verifier…) doivent être déclarées dans un fichier de constantes partagé entre tous les modules impliqués. L'utilisation de chaînes littérales dispersées est interdite.

6.3 login_hint

Lorsque l'email de l'utilisateur est connu avant la redirection vers Keycloak, transmettre le paramètre standard OIDC login_hint pour pré-remplir le formulaire Keycloak et éviter une double saisie.

6.4 Multi-organisations (Discovery)

Si l'API Discovery peut retourner plusieurs comptes pour un même email (plusieurs realms Keycloak), le frontend doit afficher un écran de sélection d'organisation avant de rediriger. Ne jamais prendre aveuglément users[0] si la réponse peut contenir plusieurs entrées.

  • Vérifier la présence d'utilisateurs avec users?.length > 0 et non avec un champ found qui peut ne pas exister selon la version de l'API.
  • Vérifier systématiquement le nom exact du champ d'identifiant retourné par l'API (customer_wid vs network_id, customer_id vs customer_wid…) et le documenter dans le contrat d'API.

6.5 URL de Keycloak côté serveur

La variable KEYCLOAK_URL (URL interne ou publique pour les appels server-side) doit être explicitement définie dans la configuration K8s. Sans elle, les appels tombent sur localhost:8080 et échouent silencieusement.

Documenter dans le .env.example la différence entre KEYCLOAK_URL (server-side) et NEXT_PUBLIC_KEYCLOAK_URL / KEYCLOAK_PUBLIC_URL (client-side / runtime).


7. Migrations Prisma (au-delà du runtime)

Les migrations de base de données doivent être gérées de manière déterministe et reproductible entre les environnements.

Règles pour les migrations Prisma

  • Le répertoire prisma/migrations/ doit contenir une migration initiale générée par prisma migrate dev qui crée l'intégralité du schéma. Les scripts SQL en vrac non gérés par Prisma ne sont pas acceptables.
  • Les migrations spécifiques (ex: index FTS, ajout de colonnes) doivent être encapsulées dans des migrations Prisma dédiées, et non sous forme de fichiers SQL isolés.
  • prisma doit figurer dans les dependencies (non devDependencies) pour que prisma migrate deploy soit disponible dans l'image de production.
  • L'entrypoint.sh exécute prisma migrate deploy avant tout démarrage de l'application — voir section 2.
  • Ne jamais importer dotenv/config dans prisma.config.ts : les variables d'environnement sont injectées par K8s.