Cosa stiamo costruendo
Un contacalorie che permette agli utenti di cercare alimenti, registrare i pasti e vedere i totali nutrizionali giornalieri. Useremo JavaScript puro e l’API di FoodBase per tenere tutto semplice e indipendente da qualsiasi framework.
Alla fine di questo tutorial avrai un’app funzionante che:
- Cerca nel nostro database di 6,1 M di prodotti alimentari
- Mostra le informazioni nutrizionali per porzione
- Tiene il totale giornaliero di calorie e macro
Prerequisiti
- Un account FoodBase (registrati gratis)
- Una chiave API dalla dashboard
- Conoscenze di base di JavaScript
Passo 1: configura il client API
Crea una funzione riutilizzabile per chiamare l’API:
const API_KEY = "food_your_key_here";
const BASE_URL = "https://foodbase.dev/v1";
async function foodApi(path) {
const res = await fetch(`${BASE_URL}${path}`, {
headers: { "X-API-Key": API_KEY },
});
if (!res.ok) {
const err = await res.json();
throw new Error(err.error || `HTTP ${res.status}`);
}
return res.json();
}
Passo 2: cerca gli alimenti
Lascia che gli utenti cerchino per nome. L’API restituisce i risultati ordinati per pertinenza:
async function searchFoods(query, limit = 10) {
const params = new URLSearchParams({ q: query, limit });
const data = await foodApi(`/foods/search?${params}`);
return data.data; // array of food items
}
// Usage
const results = await searchFoods("chicken breast");
console.log(`Found ${results.length} results`);
results.forEach(food => {
console.log(`${food.name_default} (${food.brand || "Generic"})`);
});
Passo 3: ottieni i dettagli nutrizionali
Quando l’utente seleziona un alimento, recupera i suoi dettagli completi, incluso il riepilogo nutrizionale:
async function getFoodWithNutrition(foodId) {
return foodApi(`/foods/${foodId}`);
}
const food = await getFoodWithNutrition("abc-123");
console.log(`${food.name_default}: ${food.nutrition_summary.energy_kcal} kcal/100g`);
Il nutrition_summary include i 6 macro più comuni: energy_kcal, proteins_g, carbs_g, fat_g, sugars_g e fiber_g.
Passo 4: calcola per porzione
L’API restituisce tutti i valori per 100 g. Per calcolare per porzione, moltiplica per il peso della porzione:
function calculateServing(nutritionPer100g, servingGrams) {
if (!nutritionPer100g) return null;
const factor = servingGrams / 100;
return {
calories: Math.round((nutritionPer100g.energy_kcal || 0) * factor),
protein: round1((nutritionPer100g.proteins_g || 0) * factor),
carbs: round1((nutritionPer100g.carbs_g || 0) * factor),
fat: round1((nutritionPer100g.fat_g || 0) * factor),
fiber: round1((nutritionPer100g.fiber_g || 0) * factor),
};
}
function round1(n) {
return Math.round(n * 10) / 10;
}
// Example: 100g chicken breast
const serving = calculateServing(food.nutrition_summary, 150);
// → { calories: 248, protein: 46.5, carbs: 0, fat: 5.3, fiber: 0 }
Passo 5: tieni traccia dei pasti del giorno
Salva i pasti registrati e calcola i totali progressivi:
const meals = [];
function addMeal(food, servingGrams) {
const nutrition = calculateServing(food.nutrition_summary, servingGrams);
meals.push({
name: food.name_default,
brand: food.brand,
grams: servingGrams,
...nutrition,
addedAt: new Date(),
});
return nutrition;
}
function getDailyTotals() {
return meals.reduce(
(totals, meal) => ({
calories: totals.calories + (meal.calories || 0),
protein: round1(totals.protein + (meal.protein || 0)),
carbs: round1(totals.carbs + (meal.carbs || 0)),
fat: round1(totals.fat + (meal.fat || 0)),
fiber: round1(totals.fiber + (meal.fiber || 0)),
}),
{ calories: 0, protein: 0, carbs: 0, fat: 0, fiber: 0 },
);
}
// Log a meal
addMeal(food, 150);
console.log(getDailyTotals());
// → { calories: 248, protein: 46.5, carbs: 0, fat: 5.3, fiber: 0 }
Passo 6: aggiungi la scansione dei codici a barre
Se la tua app ha accesso alla fotocamera, puoi scansionare i codici a barre e trovare i prodotti all’istante:
async function lookupBarcode(barcode) {
try {
return await foodApi(`/foods/barcode/${barcode}`);
} catch (err) {
if (err.message.includes("Not found")) {
return null; // Product not in database
}
throw err;
}
}
// After scanning a barcode with a camera library
const product = await lookupBarcode("5449000000996");
if (product) {
console.log(`Found: ${product.name_default} by ${product.brand}`);
// → "Found: Original Taste by Coca-Cola"
}
Passo 7: salva i dati
In un’app vera vorrai conservare lo storico dei pasti. Ecco un approccio semplice con localStorage:
function saveMeals() {
localStorage.setItem("meals", JSON.stringify(meals));
localStorage.setItem("mealsDate", new Date().toISOString().slice(0, 10));
}
function loadMeals() {
const today = new Date().toISOString().slice(0, 10);
const savedDate = localStorage.getItem("mealsDate");
if (savedDate === today) {
const saved = localStorage.getItem("meals");
return saved ? JSON.parse(saved) : [];
}
// New day — reset
return [];
}
Consigli per le app in produzione
- Salva in cache i risultati di ricerca sul client per ridurre le chiamate API quando gli utenti ripetono le stesse ricerche
- Usa l’endpoint bulk quando carichi lo storico dei pasti — recupera tutti i dettagli degli alimenti in una sola richiesta invece di N chiamate singole
- Imposta obiettivi giornalieri e mostra barre di avanzamento (obiettivi comuni: 2000 kcal, 50 g di proteine, 25 g di fibre)
- Gestisci bene la modalità offline — salva in cache gli alimenti visti di recente così l’app funziona senza rete
- Tieni d’occhio il tuo consumo nella dashboard per restare entro i limiti del tuo piano
Bonus: registra i pasti da una foto
Se hai crediti IA da spendere (ogni piano ne include), l’endpoint dei pasti trasforma la foto di un piatto in voci registrate con stime delle porzioni — un modo molto più comodo di registrare rispetto alla ricerca manuale:
async function analyzeMealPhoto(file) {
const form = new FormData();
form.append("image", file);
const res = await fetch("https://foodbase.dev/v1/vision/meal", {
method: "POST",
headers: { "X-API-Key": API_KEY },
body: form,
});
if (!res.ok) throw new Error(`Vision error: ${res.status}`);
const meal = await res.json();
// meal.items: [{ dish, portion_g_est, match: { estimated_kcal, ... } }]
// meal.estimated_total_kcal: number | null
return meal;
}
Le dimensioni delle porzioni sono stime dell’IA a partire da una sola foto, quindi considera i numeri un punto di partenza che l’utente può modificare — la risposta include una stringa disclaimer che dovresti mostrare. Provalo prima nel playground foto.
Prossimi passi
- Aggiungi una funzione di pianificazione dei pasti con alimenti salvati e macro obiettivo
- Integra l’API di un fitness tracker per tenere conto dell’attività fisica
- Crea un calcolatore di ricette che somma i nutrienti di più ingredienti
- Usa l’endpoint dei nutrienti dettagliati per il monitoraggio dei micronutrienti (vitamine, minerali)
Continua a leggere
Costruiscilo con FoodBase
6,1 M di prodotti, oltre 40 nutrienti e analisi degli alimenti con l'IA — gratis per iniziare, senza carta di credito.