FFFood
PWA qui transforme les repas de la semaine en liste de courses partagée, utilisable hors ligne en magasin et sans compte.
- Rôle :
- Développeur full-stack
- Période :
- Depuis juil. 2026 · en cours
- Seul

En bref pour les recruteurs
Application repas → courses → cuisson conçue seul de bout en bout, en ligne sur fffood.fr. Sécurité appliquée par la base (RLS Supabase), mode hors ligne complet, synchronisation entre deux téléphones sans compte, 137 recettes prérendues pour le SEO et tests sur toute la logique métier.
FFFood aide un foyer à décider quoi manger dans la semaine, à en tirer une liste de courses, puis à cuisiner étape par étape. Le parcours tient en quatre écrans : Proposer (choisir des plats parmi 137 recettes écrites à la main), Panier (ajuster les portions), Liste (agrégée par rayon, découpable par magasin, avec une passe « ce que j'ai déjà » avant de partir) et Cuisson (étapes, doses de l'étape, minuteurs). L'app s'installe sur le téléphone, fonctionne sans réseau en magasin et se partage entre deux personnes par un simple lien ou un code à six caractères, sans création de compte. Elle est disponible en français et en anglais.
Technologies
React
TypeScript
PostgreSQL
Vite
Node.js
Supabase
Vercel
Fonctionnalités
- Proposer des plats filtrés par temps de préparation et par tags
- Panier de la semaine avec ajustement des portions (quantités recalculées)
- Liste de courses agrégée, rangée par rayon et découpable par magasin
- Passe « ce que j'ai déjà » avant de partir faire les courses
- Liste partagée en temps réel entre téléphones, par lien ou code à 6 caractères
- Mode cuisson étape par étape avec les doses de l'étape et l'écran maintenu allumé
- Minuteurs détectés dans le texte des étapes, persistants, avec notification en arrière-plan
- Ajout de recettes par formulaire ou par collage d'un JSON généré par une IA (prompt fourni)
- Favoris déduits des plats notés « À refaire » après cuisson
- Fonctionne hors ligne et s'installe sur l'écran d'accueil
- Interface en français et en anglais
Contexte
Pour un foyer de deux personnes, le processus était éparpillé : recettes cherchées dans une conversation avec une IA, page HTML ouverte sur le téléphone pour cuisiner, liste écrite à part, courses dans deux magasins. L'objectif était de réunir tout le parcours dans une seule app, simple à utiliser, gratuite à faire tourner (aucun service payant, aucune API d'IA au runtime) et utilisable sans compte.
Architecture
SPA React + TypeScript (Vite), installable en PWA avec Workbox. Le corpus de 137 recettes est un fichier JSON statique embarqué au build, avec les traductions anglaises et un glossaire d'ingrédients dans des fichiers séparés. Chaque appareil garde un état local dans IndexedDB. Ce qui se décide à deux (liste cochée, panier, catalogue, historique de cuisson) est synchronisé via Supabase (PostgreSQL + Realtime). L'accès à un foyer passe par une session anonyme Supabase, une table d'appartenance et des fonctions RPC (code court ou UUID). Les règles RLS appliquent ce modèle côté base. Après le build, un script Node génère une page HTML statique par recette avec le balisage schema.org/Recipe et le sitemap. Ces pages sont exclues du précache et du repli SPA du service worker.
Décisions techniques
Recettes en fichier statique : pas de base pour le corpus, pas d'appel réseau pour l'afficher, une modification est un commit. Le nom canonique d'un ingrédient est la clé d'agrégation de la liste. Un lint dédié repère les quasi-doublons et les incohérences de rayon. Pas d'inventaire du placard : une passe « ce que vous avez déjà » de quinze secondes devant le frigo remplace un stock que personne ne tient à jour. Partage par lien (UUID) sans compte, mais appliqué par la base et non par le client. Les recettes connaissent des rayons, les magasins sont un réglage de l'appareil : le corpus reste valable pour tout le monde. Tests en node:assert, sans framework, ciblés sur les fonctions où une régression ne se verrait pas à l'œil.
Difficultés
Faille de sécurité corrigée : les premières règles laissaient le filtrage par foyer au client, alors que la clé anon est publique. N'importe qui pouvait lister et effacer toutes les listes. J'ai écrit une migration transactionnelle rejouable (sessions anonymes, table d'appartenance, RPC, compteur d'essais de code), testée sur une base neuve et une base peuplée, avec des scripts de vérification avant/après. Robustesse : dans une WebView Instagram ou en navigation privée, IndexedDB rejette et l'app restait sur un écran blanc. L'absence de stockage est maintenant traitée comme un stockage vide. Une lecture réseau qui échoue n'est plus confondue avec une réponse vide, ce qui évitait des doublons de catalogue et des listes republiées vides. SEO d'une SPA à une seule URL : résolu par le prérendu des 137 recettes. Mises à jour d'une PWA : vérification périodique, bascule automatique quand l'utilisateur ne regarde pas, bandeau sinon, pour ne jamais recharger en pleine cuisson.
Résultats
En ligne sur fffood.fr, utilisée chaque semaine par le foyer et partagée à de premiers utilisateurs. 137 recettes indexables avec données structurées, app bilingue FR/EN, entièrement utilisable hors ligne. Une commande de CI locale enchaîne typecheck, tests, lint du corpus, vérification des invariants (parité des traductions, crédits photo) et build.
Questions fréquentes
- Pourquoi pas de compte utilisateur ?
- Le contenu est une liste de courses : demander un compte ajoutait de la friction sans rien protéger d'important. Le foyer est identifié par un UUID partagé par lien, et la base n'ouvre l'accès qu'aux appareils qui présentent ce code.
- Comment la sécurité tient-elle sans compte ?
- Chaque appareil ouvre une session anonyme Supabase, invisible pour l'utilisateur. Une table relie cette session aux foyers auxquels elle a accès, et les règles RLS refusent toute lecture en dehors. Sans session, la clé publique ne renvoie aucune ligne.
- Pourquoi des recettes en JSON statique plutôt qu'en base ?
- Le corpus est écrit à la main et change peu. En statique, il s'affiche hors ligne, sans latence, et chaque modification est versionnée dans Git.
- As-tu utilisé l'IA pour développer ?
- Oui, Claude comme assistant de code. Les choix produit, l'architecture, la revue du code et les tests en conditions réelles sont les miens.
