Applicazione web per tracciare alimentazione e peso: ricerca su due database alimentari pubblici, scansione di codici a barre, calcolo di calorie e macro.

Frontend React 19 in TypeScript, API ASP.NET Core con Entity Framework su SQL Server. Il codice è pubblico su GitHub: quello che segue è il motivo delle scelte, non l'elenco delle funzionalità.

Due fonti dati, due schemi diversi

Gli alimenti arrivano da USDA FoodData Central e da Open Food Facts. La prima è un'API con chiave e dati curati, la seconda è aperta e alimentata dagli utenti: stesso dominio, strutture JSON che non si somigliano. L'API .NET espone due endpoint distinti e normalizza entrambe le risposte in un unico modello prima che il frontend le veda.

Open Food Facts è un database collaborativo, e si sente: lo stesso campo numerico arriva a volte come numero, a volte come stringa, a volte vuoto. La deserializzazione falliva su prodotti perfettamente validi. La soluzione è un JsonConverter scritto a mano che accetta entrambe le forme e degrada a null invece di far cadere l'intera richiesta.

Ogni chiamata esterna ha un timeout di dieci secondi e uno User-Agent identificabile, come chiede la policy di Open Food Facts. Un servizio di terze parti lento non deve diventare una ricerca che non risponde.

Il modello dati

Otto entità: utenti, misurazioni, alimenti, ingredienti, categorie, attività e le tabelle di collegamento. Il rapporto tra alimenti e ingredienti è molti-a-molti, modellato con una tabella di join a chiave composta invece che con un id surrogato: la coppia è già univoca, una colonna in più sarebbe solo altra roba da mantenere.

Le cancellazioni a cascata sono dichiarate esplicitamente nella configurazione del contesto, non lasciate alla convenzione del framework. Cancellare un utente porta via le sue misurazioni e le sue attività; cancellare un alimento non tocca l'ingrediente, che esiste per conto suo. È una regola di dominio, e sta scritta dove si legge.

Lo schema evolve per migrazioni versionate nel repository, non a mano sul database: la struttura del database è codice, e segue il codice.

Dal peso a un obiettivo giornaliero

Il fabbisogno si calcola con l'equazione di Mifflin-St Jeor per il metabolismo basale, moltiplicata per un fattore di attività che va da 1,2 per chi è sedentario a 1,9 per chi si allena ogni giorno. Sull'obiettivo si applica un taglio di 500 kcal per la perdita di peso o un surplus di 400 kcal per l'aumento: conservativo di proposito, perché è il numero che regge nel tempo.

Il calcolo parte sempre dall'ultima misurazione registrata, mai da un valore congelato sul profilo. Il peso cambia, e un obiettivo calcolato su quello di tre mesi fa è semplicemente sbagliato. Se l'utente non ha ancora inserito misurazioni il servizio restituisce un valore di partenza invece di sollevare un'eccezione: un'app di nutrizione deve funzionare anche al primo accesso.

La logica vive in un servizio separato dietro un'interfaccia, registrato in dependency injection. I controller non calcolano: ricevono, orchestrano, rispondono.

Il frontend

React 19 con TypeScript e Vite. Tailwind CSS per il layout, Material UI per i componenti di form e MUI X-Charts per l'andamento del peso nel tempo.

La scansione dei codici a barre usa html5-qrcode con la libreria ZXing: la fotocamera legge l'EAN, il codice diventa una chiamata all'endpoint prodotto di Open Food Facts e l'alimento entra nel diario senza digitare niente. È la funzione che decide se l'app viene usata davvero, perché registrare un pasto a mano costa troppo per farlo tre volte al giorno.

Perché un backend .NET dietro a React

Il frontend non chiama mai direttamente USDA o Open Food Facts. L'API fa da ponte: tiene la chiave USDA fuori dal browser, riduce due risposte diverse a un formato solo e decide cosa merita di finire in database e cosa resta di passaggio.

Gli alimenti che l'utente registra davvero vengono salvati con il loro codice a barre. La seconda volta la ricerca non esce nemmeno dal perimetro dell'applicazione: nessuna chiamata esterna, nessun timeout da gestire, nessuna dipendenza dal fatto che un servizio pubblico sia online.