Aller au contenu

Développer en local

Faire tourner un projet Fraza sur son poste avec une base, un stockage et un courriel locaux, sans compte chez un fournisseur, et éviter les pièges courants.

Un projet Fraza tourne en local avec trois services : une base PostgreSQL, un stockage compatible S3 et un moyen de lire les courriels. Aucun ne demande de compte chez un fournisseur. Avec Docker, une commande les démarre tous. Sans Docker, cette page dit quoi mettre dans .env.local pour chacun. Elle finit par les pièges qu'on rencontre en développant.

Il y a deux façons de développer :

Développement local (par défaut)Avec les données de production
Base, fichiers, courrielsSur le poste, vides au départCeux du vrai site
Ce qu'on casseRien : npm run services:reset remet tout à zéroLe vrai site
Valeurs.env.example, copié en .env.local par npm run devCelles de production, écrites dans .env.local
Pour quiTout développeur du projetLa personne qui a accès à la production

Le développement local est le cas normal. Travailler sur la production se fait en connaissance de cause : voir Développer avec les données de production.

Avec Docker

Un projet créé par @fraza/start a un compose.yaml, et son .env.example porte déjà les valeurs qui vont avec.

npm run services     # PostgreSQL, S3 local et Mailpit
npx fraza-db init    # les tables
npm run dev          # le site

C'est tout, pour le premier développeur comme pour un collègue qui clone le projet : npm run dev crée .env.local depuis .env.example s'il manque.

  • Les ports sont ceux de la machine. Au premier lancement, chaque service reçoit un port libre (5432, sinon 5433…), gardé ensuite dans .env.local avec les adresses qui le portent. Deux projets tournent ainsi côte à côte. Le site fait de même à partir de 3000, et NEXT_PUBLIC_URL suit.
  • Les courriels arrivent dans la boîte de réception de Mailpit, dont npm run services affiche l'adresse, http://localhost:8025 d'habitude.
  • Les fichiers vont dans le S3 local, dont le bucket est créé au démarrage.
  • Repartir de zéro : npm run services:reset supprime la base, les fichiers et les courriels locaux, redémarre les services et recrée les tables. Pour la base seule, npx fraza-db reset --admin la recrée avec un compte admin local, et npx fraza-db seed la remplit des données de démonstration des modules.
  • Arrêter : npm run services:stop. Les données restent.

Le détail est dans fraza-dev. Les sections qui suivent servent à qui monte ses services autrement.

La base

Un PostgreSQL 14 ou plus sur le poste, sans TLS :

FRAZA_DATABASE_URL=postgres://fraza@localhost:5432/fraza?sslmode=disable

?sslmode=disable est nécessaire : TLS est strict par défaut, en développement comme en production. Voir Configuration.

npx fraza-db init   # crée les tables, sans risque à relancer
npx fraza-db drop   # supprime toutes les tables, pour repartir de zéro

Une seule base suffit pour développer. Les tests prennent leurs propres schémas jetables sur la base de FRAZA_TEST_DATABASE_URL, qui peut être la même : voir Tests.

Le courriel

Deux façons de lire les courriels du site sans rien envoyer.

Dans le terminal, avec le fournisseur log :

FRAZA_EMAIL_PROVIDER=log
FRAZA_EMAIL_FROM_ADDRESS=[email protected]

Chaque courriel s'écrit dans le terminal de npm run dev : le code de vérification dans le sujet, les liens dans le texte. Voir Le fournisseur log.

Dans une boîte de réception locale, avec Mailpit, qui reçoit en SMTP et affiche les courriels sur http://localhost:8025 :

FRAZA_EMAIL_PROVIDER=smtp
FRAZA_EMAIL_SMTP_HOST=localhost
FRAZA_EMAIL_SMTP_PORT=1025
FRAZA_EMAIL_FROM_ADDRESS=[email protected]

Mailpit n'offre pas de chiffrement et ne demande pas d'identifiant : laisser FRAZA_EMAIL_SMTP_USER, FRAZA_EMAIL_SMTP_PASSWORD et FRAZA_EMAIL_SMTP_REQUIRE_TLS vides. Le journal avertit que l'envoi part en clair, ce qui est attendu sur localhost. Mailpit montre le rendu HTML réel du courriel, ce que log ne fait pas.

Un fournisseur log laissé en production fait échouer l'envoi : il est refusé avec NODE_ENV=production.

Le stockage

Le core parle à n'importe quel serveur compatible S3. En local, un serveur comme Versity S3 Gateway suffit, et le core l'appelle en http:// :

FRAZA_STORAGE_PROVIDER=backblaze
FRAZA_STORAGE_ENDPOINT=http://localhost:9000
FRAZA_STORAGE_REGION=us-east-1
FRAZA_STORAGE_BUCKET=fraza-local
FRAZA_STORAGE_ACCESS_KEY=fraza-local
FRAZA_STORAGE_SECRET_KEY=fraza-local-secret

http:// n'est accepté que pour un hôte local : localhost, 127.0.0.1 ou [::1]. Il n'y a donc ni certificat à générer ni NODE_EXTRA_CA_CERTS à poser. Un endpoint distant en http:// est refusé, pour ne jamais envoyer un fichier en clair sur le réseau. Voir Stockage.

FRAZA_STORAGE_REGION doit valoir la région que le serveur local attend, us-east-1 le plus souvent, sinon les signatures sont refusées.

Sans stockage, le site tourne, mais tout ce qui touche aux fichiers échoue : photo de profil, médiathèque, pièces jointes.

Les paiements

Stripe a un mode test et un CLI qui relaie les webhooks vers le poste :

stripe listen --forward-to localhost:3000/za/api/payments/stripe/webhook

Le whsec_… affiché par la commande va dans STRIPE_WEBHOOK_SECRET, et la clé sk_test_… du tableau de bord dans STRIPE_SECRET_KEY. Ces clés de test se partagent par le coffre de l'équipe, pas par git : voir Partager les variables d'un projet.

Les outils de l'admin

Sous npm run dev, l'admin a un devkit, sans rien poser : la galerie des composants, le catalogue d'icônes, le nuancier du thème, et les tâches planifiées, avec un bouton pour en lancer une sans attendre son heure. Un envoi groupé de courriels part alors tout de suite dans Mailpit. Voir DevKit.

Sans Docker, en bref

Un PostgreSQL installé sur le poste, et le fournisseur de courriel log :

FRAZA_DATABASE_URL=postgres://moi@localhost:5432/mon_site?sslmode=disable
FRAZA_TEST_DATABASE_URL=postgres://moi@localhost:5432/mon_site?sslmode=disable
FRAZA_EMAIL_PROVIDER=log

Le site tourne, les courriels s'affichent dans le terminal. Seuls les envois de fichiers échouent, tant qu'aucun S3 n'est configuré.

Développer avec les données de production

Il suffit d'écrire les valeurs de production dans .env.local : elles passent avant celles de .env.example, et npm run dev ne réécrit que les adresses locales (localhost). Une base, un bucket ou un serveur de courriel distants gardent leur valeur.

# .env.local
NEXT_PUBLIC_URL=http://localhost:3000      # jamais l'adresse du vrai site, voir plus bas
FRAZA_DATABASE_URL=postgres://site:…@db.exemple.ca:5432/site
FRAZA_ENCRYPTION_KEY=…                     # celle de production
FRAZA_STORAGE_ENDPOINT=s3.ca-east-006.backblazeb2.com
FRAZA_STORAGE_BUCKET=mon-site
FRAZA_STORAGE_ACCESS_KEY=…
FRAZA_STORAGE_SECRET_KEY=…
FRAZA_EMAIL_PROVIDER=resend
FRAZA_EMAIL_RESEND_APIKEY=…

Trois valeurs demandent de l'attention :

  • NEXT_PUBLIC_URL reste http://localhost:3000. Le site tourne sur le poste, et la vérification CSRF compare l'origine du navigateur à cette adresse. Avec https://exemple.ca, toute écriture est refusée : connexion, envoi de fichier, enregistrement. npm run dev avertit dans ce cas.
  • FRAZA_ENCRYPTION_KEY est celle de production. Une autre clé laisse illisible ce que le site a chiffré, comme les secrets enregistrés par les modules.
  • Une partie seulement peut viser la production. La base de production avec le stockage local, par exemple : les pages s'affichent, mais les images de la vraie médiathèque manquent.

Au démarrage, npm run dev nomme ce qui est hors du poste :

⚠ Données hors de ce poste : la base db.exemple.ca, le stockage s3.ca-east-006.backblazeb2.com, les courriels par Resend. Ce que fait ce serveur les touche pour de vrai.
⚠ Tâches planifiées arrêtées : la base n'est pas sur ce poste. FRAZA_CRON=true dans .env.local pour les lancer quand même.

Les tâches planifiées s'arrêtent d'elles-mêmes quand la base n'est pas sur le poste. Sans cela, le portable du développeur ferait tourner les tâches du vrai site : rappels de facture, purges, synchronisations. Le serveur de production les fait déjà. FRAZA_CRON=true dans .env.local les relance, pour déboguer une tâche par exemple. La page « Tâches planifiées » du devkit dit alors pourquoi elle est vide.

Ce qui reste réel : une photo supprimée disparaît du vrai bucket, un courriel part vraiment, un compte modifié l'est pour tous. npx fraza-db reset et seed refusent une base qui n'est pas sur le poste, sauf avec --remote.

Les valeurs de production viennent du coffre de l'équipe, fiche « production », jamais d'un fichier suivi par git. Voir Partager les variables d'un projet.

Modifier un module

Le core charge les modules une seule fois, au démarrage de next dev : leurs pages, routes, menus et permissions sont enregistrés à ce moment. Une route ou un menu modifié dans un module garde son ancienne version jusqu'au prochain démarrage.

npm run dev redémarre le serveur tout seul quand un module lié est recompilé, en quelques secondes. Un module lié, c'est un module installé par file: ou npm link, dont le dossier réel est hors de node_modules. Seul son dossier dist/ est lu : le recompiler, par npm run build ou un tsup --watch dans le module, suffit.

Un module développé dans le projet, sous modules/<nom>/ et installé par file:./modules/<nom>, est compilé par npm run dev lui-même : au démarrage, puis à chaque modification. npx fraza-modules build le compile sans lancer le site. Voir fraza-dev et Cycle de vie d'un module.

Un serveur lancé par next dev directement, sans npm run dev, ne redémarre pas : l'arrêter et le relancer après une modification.

Un module lié hors du dossier du projet, un dépôt voisin installé par npm link ou file:../module-x par exemple, n'est pas trouvé par Turbopack, qui ne lit rien hors de la racine du projet : « Module not found: Can't resolve 'module-x' ». Poser la racine sur le dossier qui contient les deux, dans next.config.mjs :

import path from 'node:path';

const nextConfig = {
  turbopack: {
    root: path.resolve('..'),
    // les règles existantes restent
  },
};

Un module placé dans le projet, sous modules/ ou vendor/, n'en a pas besoin.

Changer un FRAZA_MODULE_* demande aussi un redémarrage : fraza-modules sync, lancé par npm run dev, relit les variables et régénère app/.fraza/.

Pièges courants

SymptômeCauseSolution
docker compose is not availableDocker absent ou arrêtéDémarrer Docker, ou suivre « Sans Docker, en bref ».
Pages qui se rechargent en boucle (« Fast Refresh » ou « Compiled in … ms » sans fin), surtout après l'ajout, le retrait ou le renommage d'un moduleCache Turbopack incohérentfraza-modules sync vide .next/dev quand la liste des modules change. Si la boucle persiste : arrêter npm run dev, supprimer .next/dev, relancer.
Se connecter à un projet déconnecte un autre projet ouvert sur localhostLes cookies ne dépendent pas du port : les deux projets partagent le cookie fraza_sessionLancer chaque projet par npm run dev, qui donne au cookie le nom du projet. Sinon, poser un FRAZA_AUTH_SESSION_COOKIE_NAME propre à chaque projet.
Les écritures sont refusées (formulaires, API)NEXT_PUBLIC_URL ne correspond pas à l'adresse du navigateur : next dev passé sur 3001, ou l'adresse du vrai site recopiée avec les valeurs de productionLancer le site par npm run dev, qui fait suivre le port. Sinon, mettre NEXT_PUBLIC_URL=http://localhost:3000 dans .env.local, et retirer un .env.development.local qui la fixerait.
Aucune tâche planifiée ne tourne sous npm run devLa base n'est pas sur le poste : npm run dev pose FRAZA_CRON=falseVoulu. FRAZA_CRON=true dans .env.local les relance.
Une page publique de module voit un registre vide, alors que le démarrage l'a rempliÉtat gardé dans une variable de module : la page et le démarrage sont compilés séparémentRanger l'état sur globalThis. Voir Registres et singletons.
Un module installé n'apparaît pasFRAZA_MODULE_<NOM>=true absent de .env.local au moment de npm install ou de npm run devPoser la variable, puis relancer npm run dev.
« Requête invalide » sur un formulaire de connexionAnti-robot : champ caché rempli par un gestionnaire de mots de passe, ou formulaire envoyé trop viteRecharger la page, ou utiliser une fenêtre privée.
Courriel ou fichier en échec sans message clairVariable mal saisienpx fraza-doctor confronte .env.local au catalogue des variables.

Sur cette page