Ce que nous allons construire
Un suivi de calories qui permet aux utilisateurs de rechercher des aliments, d’enregistrer leurs repas et de voir leurs totaux nutritionnels quotidiens. Nous utiliserons du JavaScript pur et l’API FoodBase pour rester simples et indépendants de tout framework.
À la fin de ce tutoriel, vous aurez une application fonctionnelle qui :
- Recherche dans notre base de 6,1 M de produits alimentaires
- Affiche les informations nutritionnelles par portion
- Suit les totaux quotidiens de calories et de macronutriments
Prérequis
- Un compte FoodBase (inscription gratuite)
- Une clé d’API depuis le tableau de bord
- Des connaissances de base en JavaScript
Étape 1 : configurer le client d’API
Créez une fonction réutilisable pour appeler 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();
}
Étape 2 : rechercher des aliments
Laissez les utilisateurs chercher par nom. L’API renvoie les résultats classés par pertinence :
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"})`);
});
Étape 3 : obtenir le détail nutritionnel
Quand l’utilisateur sélectionne un aliment, récupérez ses détails complets, y compris le résumé nutritionnel :
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`);
Le nutrition_summary contient les 6 macronutriments les plus courants : energy_kcal, proteins_g, carbs_g, fat_g, sugars_g et fiber_g.
Étape 4 : calculer par portion
L’API renvoie toutes les valeurs pour 100 g. Pour calculer par portion, multipliez par le poids de la portion :
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 }
Étape 5 : suivre les repas du jour
Stockez les repas enregistrés et calculez les totaux cumulés :
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 }
Étape 6 : ajouter la lecture de codes-barres
Si votre application a accès à la caméra, vous pouvez scanner des codes-barres et retrouver les produits instantanément :
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"
}
Étape 7 : conserver les données
Pour une vraie application, vous voudrez sauvegarder l’historique des repas. Voici une approche simple avec 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 [];
}
Conseils pour les applications en production
- Mettez en cache les résultats de recherche côté client pour réduire les appels d’API quand les utilisateurs refont les mêmes recherches
- Utilisez l’endpoint bulk pour charger l’historique des repas — récupérez tous les détails des aliments en une seule requête au lieu de N appels individuels
- Définissez des objectifs quotidiens et affichez des barres de progression (objectifs courants : 2000 kcal, 50 g de protéines, 25 g de fibres)
- Gérez le mode hors ligne avec élégance — mettez en cache les aliments consultés récemment pour que l’application fonctionne sans réseau
- Surveillez votre consommation dans le tableau de bord pour rester dans les limites de votre offre
Bonus : enregistrer ses repas à partir d’une photo
Si vous avez des crédits IA à dépenser (chaque offre en inclut), l’endpoint repas transforme une photo d’assiette en éléments enregistrés avec des estimations de portions — une façon beaucoup plus simple d’enregistrer ses repas que de chercher à la main :
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;
}
Les tailles de portion sont des estimations de l’IA à partir d’une seule photo ; considérez donc les chiffres comme un point de départ que l’utilisateur peut ajuster — la réponse inclut une chaîne disclaimer que vous devez afficher. Essayez d’abord dans le playground photo.
Prochaines étapes
- Ajoutez une fonctionnalité de planification de repas à partir d’aliments enregistrés et de macronutriments cibles
- Intégrez l’API d’un tracker d’activité pour prendre en compte l’exercice
- Créez un calculateur de recettes qui additionne les nutriments de plusieurs ingrédients
- Utilisez l’endpoint de nutriments détaillés pour le suivi des micronutriments (vitamines, minéraux)
À lire aussi
Construisez-le avec FoodBase
6,1 M de produits, plus de 40 nutriments et l'analyse alimentaire par IA — gratuit pour commencer, sans carte bancaire.