@crvouga/mockingbird-service-edamam

中文文档
3.11.0发布于 2026/11/11MITTypeScriptES Module构建来源已验证近 30 天下载 2876

Stateful emulator of the Edamam APIs our apps call: food-database parser, nutrients and image analysis, nutrition analysis, recipe search v2, meal-planner select and shopping lists, over a built-in food and recipe corpus.

原始使用文档
文档目录7 节
  1. Install
  2. Usage
  3. Routes
  4. Admin (beyond the standard contract)
  5. Namespaces
  6. Deliberately not modelled
  7. API

@crvouga/mockingbird-service-edamam

Local emulators. Real API contracts. Part of Mockingbird.

Stateful emulator of the Edamam APIs our apps call, answering from a built-in food and recipe corpus: the Food Database v2 parser (text and UPC), nutrients and image recognition, Nutrition Analysis (nutrition-data, nutrition-details), Recipe Search v2 (search with filters and _cont paging, by URI, by id), the Meal Planner v1 select, and Shopping List v2. Nutrition logging, barcode scans, photo logging, recipe search, meal plans and grocery lists then work in tests without Edamam keys, quotas or network.

  • Operation coverage: SUPPORT.md
  • The contract (openapi.yaml) is hand-authored from Edamam's per-API docs and our consumers: metrics/adapters/outbound/edamam-nutrition.adapter.ts, meal-planning/adapters/outbound/edamam-meal-planning.adapter.ts, and the Python chat service's tools/nutrition/client.py.

Install

npm install -D @crvouga/mockingbird-service-edamam

ESM only. Node >= 22 or Bun >= 1.2. No native dependencies. Serve it with npx mockingbird-edamam serve, createServer from ./server (Node), or createRuntime with any Fetch server.

Usage

Both backend adapters hardcode https://api.edamam.com (seam: a base-URL env for EDAMAM_BASE_URL / EDAMAM_MEAL_BASE_URL, and the Python client's EDAMAM_BASE_URL). Keys can be any values (EDAMAM_FOOD_APP_ID/KEY or EDAMAM_APP_ID/KEY, EDAMAM_MEAL_APP_ID/KEY, the Python client's edamam_* settings); without them our adapters report unavailable and never call out.

npx mockingbird-edamam serve --port 8824
import { createServer } from "@crvouga/mockingbird-service-edamam/server"

// Test setup: the emulator. The application gets EDAMAM_BASE_URL = server.url.
const server = await createServer()
const baseUrl = server.url

// Application code: the Food Database request it sends to api.edamam.com.
const query = new URLSearchParams({
  app_id: "a",
  app_key: "k",
  ingr: "2 large eggs",
  "nutrition-type": "logging",
})
const response = await fetch(`${baseUrl}/api/food-database/v2/parser?${query}`)
const parsed = await response.json()
// → {text, parsed: [{food: {foodId: "food_egg", label: "Egg", nutrients: {ENERC_KCAL: 143, …}},
//     quantity: 2, measure: {uri: "…#Measure_large", label: "Large", weight: 50}}], hints: […]}
await server.close()

Routes

Every call takes app_id and app_key as query parameters (the meal planner and shopping list also accept Authorization: Basic app_id:app_key, which our adapter sends).

RouteBehaviour
GET /api/food-database/v2/parseringr is parsed into a leading quantity ("2", "1/2", "a", "two"), a measure the food has ("cup", "large", "slice", "g", "oz") and the best-matching food → parsed[]; every food sharing a word → hints[] with measures. Filters: categoryLabel (food drops meals), category, health (repeatable), calories (per 100 g), nutrients[CODE] (per 100 g, see below). upc looks up a packaged food (unknown UPC 404).
POST /api/food-database/v2/nutrients{ingredients: [{quantity, measureURI, foodId}]} → calories, totalWeight, totalNutrients, totalDaily, dietLabels, healthLabels, ingredients[].parsed[]; an unknown food or measure is 422.
POST /api/food-database/nutrients-from-image?beta=true{image: data URL or http URL} → {parsed: {food, quantity, measure}, recipe: {label, calories, totalNutrients}}, deterministic per image (or pinned with PUT /__admin/vision).
GET /api/nutrition-data?ingr=One ingredient line; unparsable is 422.
POST /api/nutrition-details{ingr: [...], title?, yield?}; no lines is 422, any unparsable line 555.
GET /api/recipes/v2type required; q, health, diet, mealType, dishType, cuisineType, excluded (repeatable), calories and nutrients[CODE] ranges per serving, time, random, imageSize. 20 per page; _links.next.href carries _cont.
GET /api/recipes/v2/by-uriUp to 20 uri parameters; unknown URIs are skipped.
GET /api/recipes/v2/{id}One recipe (the _links.self target).
POST /api/meal-planner/v1/{app_id}/select{size, plan: {accept, fit, exclude, sections: {Breakfast: {…}, …}}} → {status: OK | INCOMPLETE, selection: [{sections: {<name>: {assigned, _links}}}]}. Each section gets a recipe satisfying its and the plan's accept predicates (health, meal, dish), its per-serving fit, and exclude, varying by day; an unfillable section has no assigned and the status is INCOMPLETE. A plan without sections is 400.
POST /api/shopping-list/v2{entries: [{quantity, measure?: Measure_serving, item: recipe uri}]} → ingredients aggregated per food in grams (Measure_serving scales by servings over the recipe's yield); ?shopping-cart=true&beta=true adds _links.shopping-cart.

Errors: Food Database and Nutrition Analysis answer {status: "error", error, message}; Recipe Search, Meal Planner and Shopping List answer [{errorCode, message, params}].

Corpus (DEFAULT_FOODS, DEFAULT_RECIPES): 18 foods with per-100 g nutrients and measures (including a meal, two packaged products with UPCs 850000000012 and 850000000036, and one UPC 850000000029 with no nutrition data) and 8 recipes built from them, so recipe totals, per-serving values and shopping lists agree with the food database.

Admin (beyond the standard contract)

RouteEffect
GET / POST /__admin/foodsList foods, or add one ({foodId, label, nutrients, measures: [{uri, label, weight}], upc?, brand?, category?, categoryLabel?, healthLabels?}).
GET / POST /__admin/recipesList recipes, or add one ({id, label, yield, ingredients: [{foodId, quantity, measure, text}], mealType?, dishType?, cuisineType?, dietLabels?, healthLabels?, cautions?, totalTime?}).
PUT /__admin/vision{foodId, quantity?, measure?} pins what image recognition returns; {notFound: true} recognises nothing; null restores the default.
GET/PUT /__admin/settings{apps?: [{appId, appKey}], requireAccountUser?}.

Fault presets (POST /__admin/faults {"preset": "<name>", "count"?: n}): rate_limited (429), payment_required (402), unauthorized (401), server_error (500), parser_schema_drift, recipe_quality (555), vision_not_found, meal_plan_incomplete, meal_plan_timeout, slow (12 s, past our 10 s timeouts), connection_drop.

Namespaces

x-mockingbird-namespace, a /__admin/ns/<name> prefix on the base URL (works for the nutrition adapter and the Python client, which concatenate paths; the meal adapter resolves paths with new URL(endpoint, base), which drops a prefix), or by application id: PUT /__admin/credentials {"credentials": {"<app_id>": "<namespace>"}}.

Deliberately not modelled

  • Edamam's NLP and databases: the parser understands leading quantities, known measures and corpus food names only; real food and recipe content needs a recorded corpus.
  • Parser nutrients[CODE] is a best guess, not oracle-verified (vendor docs only say the Food Database can "search for food by given nutrient quantity"; the query syntax, the error for a malformed range and any plan restriction were never observed live). The emulator reads nutrients[PROCNT]=20+, =5 (at most) and =5-15 (inclusive) like calories, against the food's per-100 g nutrients, and a raw + that decoded to a space still means "or more". Several codes, a repeated code and health / calories / category / categoryLabel all must hold; nothing matching is 200 with hints: [] and parsed: []. A food with no value for the code never matches. Any code shaped like a nutrient code is accepted, but the built-in corpus carries only ENERC_KCAL, PROCNT, FAT, CHOCDF, FIBTG and SUGAR, so codes such as NA or FE match only foods seeded with POST /__admin/foods that carry them (an emulator limitation, not a vendor answer). A malformed range or code is 400 bad_request. The upc lookup ignores the filters, as it does for calories.
  • Food Database autocomplete, brand search and nutrients qualifiers; Recipe Search user recipes (type=user), field= projection and images other than the URL.
  • Meal Planner plan-level fit beyond a day-calorie check, mark weighting, nested sub-sections, and shopping-cart checkout pages.

API

ExportKindDescription
EdamamAPIclassThe in-process emulator: fetch(request), reset(), addFood(food), addRecipe(seed), recipes(). Options: sqlite, now, namespace, foods, recipes, settings.
createRuntimefunctionThe emulator with the full service contract (health, admin, namespaces, credentials, presets). Options: foods, recipes, settings, clock, seed, adminKey, onLog, sqlite.
EDAMAM_PRESETSobjectEvery named fault preset.
EDAMAM_NAMESPACEstringThe service name, "edamam".
ACCOUNT_USER_HEADERstringedamam-account-user.
DEFAULT_FOODS, DEFAULT_RECIPESarraysThe built-in corpus.
MEASURE_URI, RECIPE_URI, NUTRIENTSvaluesEdamam's measure and recipe URI prefixes, and the nutrient codes served.
buildRecipefunctionA recipe in Recipe Search v2 shape from a seed and the food corpus.
parseLinefunctionThe ingredient-line parser.
perServingfunctionA recipe's per-serving value of a nutrient code.
appIdCredentialfunctionThe app_id a request carries (how credentials map to namespaces).
foodError, recipeErrorsfunctionsBuild each API family's error response.
document, operationIds, supportedOperationIdsvaluesThe OpenAPI contract and its operation ids.
createServer, serveTarget, DEFAULT_PORT (./server)NodeServe over node:http; the serve CLI target (--app, --require-account-user); port 8824.

Part of mockingbird.

版本与发布记录

@crvouga/mockingbird-service-edamam 共发布 41 个版本,首次发布于 2026/09/23;过去 12 个月发布了 41 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
3.11.0latest2026/11/11今天
3.10.82026/11/038 天前
3.10.72026/11/038 天前
3.10.62026/11/038 天前
3.10.52026/11/038 天前
3.10.42026/11/038 天前

关于 @crvouga/mockingbird-service-edamam 的常见问题

@crvouga/mockingbird-service-edamam 是什么?

Stateful emulator of the Edamam APIs our apps call: food-database parser, nutrients and image analysis, nutrition analysis, recipe search v2, meal-planner select and shopping lists, over a built-in food and recipe corpus.

如何安装 @crvouga/mockingbird-service-edamam?

在项目目录运行 npm install @crvouga/mockingbird-service-edamam,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 mockingbird-edamam,可以用 npx mockingbird-edamam 直接运行。

@crvouga/mockingbird-service-edamam 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@crvouga/mockingbird-service-edamam 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@crvouga/mockingbird-service-edamam 可以商用吗?

@crvouga/mockingbird-service-edamam 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@crvouga/mockingbird-service-edamam 的下载量有多少?

根据 npm 官方下载统计,@crvouga/mockingbird-service-edamam 在 2026/10/11 – 2026/11/09 这 30 天内被下载 2,876 次,日均约 96 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@crvouga/mockingbird-service-edamam 还在维护吗?

最新版本 3.11.0 发布于 2026/11/11(今天);至今共发布 41 个版本,其中过去 12 个月发布了 41 个;首次发布于 2026/09/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。