import { getSiteSetting } from "@/lib/site-settings"; export type UsdaNutrientsPer100g = { calories: number; proteinG: number; carbsG: number; fatG: number; fiberG: number; sodiumMg: number; }; // Standard USDA nutrient numbers (stable across FoodData Central datasets, // unlike nutrientId which can vary) — see // https://fdc.nal.usda.gov/portal-data/external/nutrientsList const NUTRIENT_NUMBERS: Record = { "208": "calories", "203": "proteinG", "205": "carbsG", "204": "fatG", "291": "fiberG", "307": "sodiumMg", }; /** Looks up an ingredient's nutrition facts per 100g via USDA FoodData * Central. Restricted to the SR Legacy and Survey (FNDDS) datasets — the * two best suited to generic/raw recipe ingredients ("flour", "olive * oil"), unlike Branded (packaged products, already covered by the * Open Food Facts barcode lookup in pantry scanning) or Foundation * (narrower coverage). Returns null if unconfigured, no usable match, or * the request fails — callers must treat this as best-effort and fall * back to AI estimation. */ export async function lookupUsdaNutrients(query: string): Promise { const apiKey = await getSiteSetting("USDA_API_KEY"); if (!apiKey) return null; try { const url = new URL("https://api.nal.usda.gov/fdc/v1/foods/search"); url.searchParams.set("api_key", apiKey); url.searchParams.set("query", query); url.searchParams.set("pageSize", "3"); url.searchParams.set("dataType", "SR Legacy,Survey (FNDDS)"); const res = await fetch(url.toString(), { signal: AbortSignal.timeout(8000) }); if (!res.ok) return null; const data = (await res.json()) as { foods?: { foodNutrients?: { nutrientNumber?: string; value?: number }[] }[]; }; const food = data.foods?.[0]; if (!food?.foodNutrients) return null; const result: Partial = {}; for (const n of food.foodNutrients) { const key = n.nutrientNumber ? NUTRIENT_NUMBERS[n.nutrientNumber] : undefined; if (key && typeof n.value === "number") result[key] = n.value; } // Energy is the load-bearing field — a "match" with no calorie value // isn't usable, treat it the same as no match. if (result.calories == null) return null; return { calories: result.calories, proteinG: result.proteinG ?? 0, carbsG: result.carbsG ?? 0, fatG: result.fatG ?? 0, fiberG: result.fiberG ?? 0, sodiumMg: result.sodiumMg ?? 0, }; } catch (err) { console.error("[usda] lookupUsdaNutrients failed", { query, message: err instanceof Error ? err.message : err }); return null; } }