@molecule/api-nutrition-database
Core interface · nutrition-database · API (Node) · v1.0.3 · Apache-2.0
Nutrition + barcode lookup core interface (free-text search, barcode lookup, normalized per-100g + per-serving nutrition facts).
npm install @molecule/api-nutrition-databaseHow it works
@molecule/api-nutrition-database is the nutrition-database core interface on the API (Node) side: the API your app calls, with no vendor inside.
Choose the implementation by bonding one of its 1 provider: @molecule/api-nutrition-database-open-food-facts.
import { setProvider, searchFood, getFoodByBarcode } from '@molecule/api-nutrition-database'
import { provider as openFoodFacts } from '@molecule/api-nutrition-database-open-food-facts'
setProvider(openFoodFacts)
const results = await searchFood('granola bar', { limit: 10 })
const lookup = await getFoodByBarcode('3017620422003')Providers (1): @molecule/api-nutrition-database-open-food-facts
Works with: @molecule/api-bond, @molecule/api-i18n
Reference
Provider-agnostic nutrition-database interface for molecule.dev.
Defines the NutritionDatabaseProvider interface for food /
product nutrition lookups (free-text search + barcode lookup + id
lookup). Bond packages (Open Food Facts, USDA FoodData Central,
Nutritionix, etc.) implement this interface. Application code uses the
convenience functions (searchFood, getFoodByBarcode, getFood)
which delegate to the bonded provider.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setProvider, searchFood, getFoodByBarcode } from '@molecule/api-nutrition-database'
import { provider as openFoodFacts } from '@molecule/api-nutrition-database-open-food-facts'
setProvider(openFoodFacts)
const results = await searchFood('granola bar', { limit: 10 })
const lookup = await getFoodByBarcode('3017620422003')
Type
core
Installation
npm install @molecule/api-nutrition-database @molecule/api-bond @molecule/api-i18n
API
Interfaces
NutritionFacts
A normalized set of nutrient values for a fixed reference quantity.
Every provider returns the same field set so application code never
branches on which provider supplied the data. Fields the upstream API
does not provide are null rather than missing or 0 — null means
"unknown", 0 means "measured zero".
Energy values are reported as kilocalories ("Calories" in US food labelling) — this matches the US FDA Nutrition Facts panel and the majority of consumer-facing nutrition apps. Mass-based fields are in grams unless otherwise noted.
interface NutritionFacts {
/**
* Reference quantity these nutrient values describe (e.g. `100` g for
* "per 100g" panels, `30` g for a typical serving). Always positive.
*/
referenceQuantity: number
/**
* Unit the `referenceQuantity` is expressed in.
*
* `'g'` for solids, `'ml'` for liquids. Provider responses that mix
* units within a single product (rare) are normalized to the dominant
* unit on the product label.
*/
referenceUnit: 'g' | 'ml'
/**
* Energy in kilocalories ("Calories") for `referenceQuantity`.
* `null` if the provider did not supply this value.
*/
calories: number | null
/**
* Protein in grams for `referenceQuantity`.
* `null` if the provider did not supply this value.
*/
protein: number | null
/**
* Total fat in grams for `referenceQuantity`.
* `null` if the provider did not supply this value.
*/
fat: number | null
/**
* Saturated fat in grams for `referenceQuantity`.
* `null` if the provider did not supply this value.
*/
saturatedFat: number | null
/**
* Total carbohydrates in grams for `referenceQuantity`.
* `null` if the provider did not supply this value.
*/
carbs: number | null
/**
* Sugars (a sub-component of carbohydrates) in grams for
* `referenceQuantity`.
* `null` if the provider did not supply this value.
*/
sugar: number | null
/**
* Dietary fiber in grams for `referenceQuantity`.
* `null` if the provider did not supply this value.
*/
fiber: number | null
/**
* Sodium in milligrams for `referenceQuantity`.
* `null` if the provider did not supply this value.
*
* Some upstream APIs report sodium in grams or report salt instead
* (salt = sodium × 2.5). Providers MUST normalize to milligrams of
* sodium before returning.
*/
sodium: number | null
}
ServingSize
Per-serving size description, when the upstream label exposes one.
Many products describe both a "per 100g/100ml" panel and a "per
serving" panel. FoodNutrition surfaces both — per100 for the
normalized comparable basis, perServing for the user-facing
label-as-printed.
interface ServingSize {
/**
* Quantity of one serving (e.g. `30` for a 30g serving).
*/
quantity: number
/**
* Unit the `quantity` is expressed in (`'g'` or `'ml'`).
*/
unit: 'g' | 'ml'
/**
* Free-text serving description as printed on the label
* (e.g. `'1 cup (240ml)'`, `'2 cookies (30g)'`). May be the empty
* string when the upstream API does not supply free-text.
*/
label: string
}
FoodNutrition
Normalized food record returned by every nutrition-database provider.
Always carries a per-100g/per-100ml panel (nutrition) so totals
can be computed from any quantity. perServing (when present)
describes the as-printed per-serving panel.
interface FoodNutrition {
/**
* Provider-specific identifier suitable for `NutritionDatabaseProvider.getFood`.
*/
id: FoodId
/**
* Display name of the food / product. The empty string when the
* upstream record has no name.
*/
name: string
/**
* Brand name, when supplied by the upstream record. `null` for
* generic / unbranded entries.
*/
brand: string | null
/**
* Product barcode (GTIN/EAN/UPC) when the upstream record has one.
* `null` for generic foods (e.g. raw ingredients in USDA).
*/
barcode: Barcode | null
/**
* Per-100g (solids) or per-100ml (liquids) nutrition panel.
*
* This is the canonical comparable basis — application code that needs
* to compute totals for an arbitrary serving size MUST scale these
* values rather than scaling `perServing`.
*/
nutrition: NutritionFacts
/**
* Per-serving nutrition panel, when the upstream label supplies a
* serving size. `null` for records with only a per-100g panel.
*/
perServing: (NutritionFacts & { serving: ServingSize }) | null
/**
* Stable URL of an image of the product / food, when supplied. `null`
* otherwise. Providers SHOULD prefer a front-of-pack image.
*/
imageUrl: string | null
/**
* Free-text ingredient list as printed on the label (e.g.
* `'water, sugar, milk solids, ...'`). The empty string when not
* supplied by the upstream record.
*/
ingredientsText: string
/**
* Identifier of the upstream provider that produced this record
* (e.g. `'open-food-facts'`, `'usda'`). Useful for telemetry and
* cache-key namespacing.
*/
source: string
}
SearchFoodOptions
Options accepted by NutritionDatabaseProvider.searchFood.
interface SearchFoodOptions {
/**
* Maximum number of rows to return. Implementations MAY clamp to
* whatever upper bound their upstream API enforces.
*/
limit?: number
/**
* Page number (1-indexed) when paginating. Implementations that do
* not paginate MAY ignore this.
*/
page?: number
}
NutritionDatabaseProvider
Nutrition-database provider interface.
All nutrition-database providers (Open Food Facts, USDA, Nutritionix,
etc.) implement this interface. The interface is deliberately minimal
so providers with very different upstream APIs can satisfy it
identically — only free-text search, barcode lookup, and id lookup are
required. Implementations whose ids ARE barcodes (e.g. Open Food Facts)
may simply alias getFood to getFoodByBarcode.
interface NutritionDatabaseProvider {
/**
* Free-text search for foods / products.
*
* @param query - Free-text query (product name, brand, ingredient).
* @param options - Pagination and result-size options.
* @returns Array of normalized food records, ordered by upstream
* relevance. May be empty when no records match.
*/
searchFood(query: string, options?: SearchFoodOptions): Promise<FoodNutrition[]>
/**
* Look up a food / product by GTIN/EAN/UPC barcode.
*
* @param barcode - Product barcode in canonical decimal-string form.
* @returns The matching food record, or `null` when the upstream
* database has no entry for the barcode.
*/
getFoodByBarcode(barcode: Barcode): Promise<FoodNutrition | null>
/**
* Look up a food / product by provider-specific id.
*
* @param id - Provider-specific identifier previously returned by
* `searchFood` or `getFoodByBarcode`.
* @returns The matching food record, or `null` when no record exists.
*/
getFood(id: FoodId): Promise<FoodNutrition | null>
}
Types
FoodId
Provider-specific food identifier.
Some providers (Open Food Facts) use the product barcode as the id;
others (USDA, Nutritionix) use opaque numeric or string identifiers.
Treat as an opaque string and only feed values previously returned by
the same provider back into NutritionDatabaseProvider.getFood.
type FoodId = string
Barcode
GTIN/EAN/UPC barcode in canonical decimal-string form.
Conventionally between 8 and 14 digits with no separators. Implementations SHOULD accept the value verbatim; callers SHOULD strip whitespace before passing it in.
type Barcode = string
Functions
setProvider(provider)
Registers a nutrition-database provider as the active singleton.
Called by bond packages
(e.g. @molecule/api-nutrition-database-open-food-facts) during
application startup.
function setProvider(provider: NutritionDatabaseProvider): void
provider— The nutrition-database provider implementation to bond.
getProvider()
Retrieves the bonded nutrition-database provider, throwing if none is configured.
function getProvider(): NutritionDatabaseProvider
Returns: The bonded nutrition-database provider.
hasProvider()
Checks whether a nutrition-database provider is currently bonded.
function hasProvider(): boolean
Returns: true if a nutrition-database provider is bonded.
searchFood(query, options)
Free-text search for foods / products via the bonded provider.
function searchFood(query: string, options?: SearchFoodOptions): Promise<FoodNutrition[]>
query— Free-text query (product name, brand, ingredient).options— Pagination and result-size options.
Returns: Array of normalized food records, ordered by upstream relevance.
getFoodByBarcode(barcode)
Look up a food / product by GTIN/EAN/UPC barcode via the bonded provider.
function getFoodByBarcode(barcode: string): Promise<FoodNutrition | null>
barcode— Product barcode in canonical decimal-string form.
Returns: The matching food record, or null when no record exists.
getFood(id)
Look up a food / product by provider-specific id via the bonded provider.
function getFood(id: string): Promise<FoodNutrition | null>
id— Provider-specific identifier previously returned bysearchFoodorgetFoodByBarcode.
Returns: The matching food record, or null when no record exists.
Available Providers
| Provider | Package |
|---|---|
| Open Food Facts | @molecule/api-nutrition-database-open-food-facts |
Injection Notes
Requirements
Peer dependencies:
@molecule/api-bond^1.0.1@molecule/api-i18n^1.0.1
Runtime Dependencies
@molecule/api-bond@molecule/api-i18n
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
- Searching a known food (e.g.
searchFood('banana')) returns the REAL matching item, and the UI renders plausible nutrition facts — calories and macros (protein/fat/carbs) in sane ranges for that food, never 0, null, or NaN where the food obviously has them. Never mock the provider. - Different foods return different, plausible records — search two distinct foods (e.g. "banana" vs "cheddar cheese") and confirm their calories/macros actually differ; you are not re-rendering one cached row.
- Barcode lookup:
getFoodByBarcode()for a known barcode (e.g. '3017620422003') resolves to the RIGHT product (matching name / brand) in the UI — not a near-miss or a substituted item. - Not-found is honest: an unknown search term returns an empty result
and an unknown barcode returns
null; the UI shows a clear "not found" state, never a wrong/substituted item and never a crash. - Portion scaling is mathematically correct: the app computes an
arbitrary serving by scaling the per-100g/ml
nutritionpanel (NOTperServing), so 2x the grams renders exactly 2x the calories and every macro. Change the serving size in the UI and verify the numbers scale linearly — the math is right, not merely non-zero. - CORRECTNESS —
null(nutrient unknown) is never shown or summed as0(measured zero). A food missing a nutrient renders "—"/unknown, and that item is excluded from (or flagged in) any diet total — presenting null as 0 silently understates the total. A logged meal/day total equals the sum of its items' scaled macros; re-add the numbers by hand and they match. - A provider / upstream failure surfaces as a visible error in the UI (not a blank panel or a silently-zeroed row), and the provider API key stays server-side: search/lookup go through the app's API, and the key never appears in the client bundle or a browser network request (this package is SERVER-ONLY — see the browser guard).