StockFlow
Gestion de stock, ventes et créances pour une équipe de vendeurs.
- Année
- 2026
- Statut
- Projet personnel
- Stack
- TypeScriptNext.js 16React 19Tailwind 4shadcn/uiSupabasePostgreSQLDocker
Le problème
Suivre à la main les ventes, le stock et les créances de plusieurs vendeurs devient vite ingérable : le stock ne correspond plus au réel, les totaux dépendent de qui fait le calcul, et personne ne sait exactement qui doit combien. Il fallait une application où ces chiffres ne puissent pas diverger — y compris si quelqu'un cherche à les faire diverger.
L'architecture
Une application Next.js qui ne parle à sa base que depuis le serveur, adossée à une instance Supabase. Le navigateur ne reçoit que du HTML et des actions serveur : aucune clé, aucune adresse de base de données, donc rien qu'il puisse interroger de lui-même.
Les décisions qui comptent
- 01
L'autorisation vit dans la base, pas dans l'interface
Chaque table a ses politiques d'accès, chaque fonction d'écriture commence par une garde, et les écritures directes sont révoquées. Le test que je m'applique avant d'ajouter un contrôle : est-ce qu'on pourrait supprimer entièrement l'interface sans ouvrir une faille ? Si la réponse est non, le contrôle est au mauvais endroit.
- 02
Aucun calcul métier en TypeScript
Totaux, coûts, marges, dettes, contrôles de stock : tout est calculé en SQL. Les montants affichés pendant la saisie ne sont qu'indicatifs, celui qui fait foi est recalculé par la base. Sans cette règle, un vendeur pourrait soumettre un total qui l'arrange, et deux implémentations du même calcul finiraient de toute façon par diverger.
- 03
Aucune clé d'accès dans le navigateur
Les variables d'environnement ne portent pas le préfixe qui les rendrait publiques : elles sont lues à l'exécution et jamais intégrées au code envoyé au client. Vérifié plutôt que supposé — les 36 scripts chargés par la page ont été inspectés un par un, et l'image Docker ne contient aucun secret.
- 04
Une règle d'accès qui se rappelait elle-même
La politique qui protège la table des profils appelle une fonction qui doit lire cette même table. En lecture normale, la politique se redéclenche à l'intérieur d'elle-même et la requête part en boucle infinie. La fonction est donc déclarée pour s'exécuter avec les droits de son propriétaire, ce qui lui permet de lire la table sans repasser par le contrôle d'accès.