Skip to content
Recipe API

Nutrient Unit Validation Is a Runtime Contract

Recent Open Food Facts changes around nutrient-unit validation and SDK API-version support show why recipe and nutrition APIs should treat units, preparation states, and validation scope as explicit contracts instead of UI cleanup logic.

nutritionapi-designvalidationdeveloper-experiencedata-modeling

The trend: small nutrition edits are becoming distributed system events

A nutrition table looks like a form. In production, it is a contract between editors, enrichment workers, SDKs, search indexes, personalization rules, and apps that calculate meals, shopping lists, and dietary constraints.

That showed up clearly in two fresh Open Food Facts changes from the last week. On August 3, Open Food Facts fixed a nutrient-unit change bug where changing one unit on the product edit page could freeze the UI for roughly ten seconds because the handler revalidated every nutrient field, sometimes several times, instead of only the affected nutrient group. The same commit describes important nutrition-specific dependencies: sugars are validated against carbohydrates, and saturated fat is validated against fat, so not every unit change can be isolated to a single scalar field.

A few days earlier, the Open Food Facts Dart SDK added explicit API 3.3 and 3.4 support. The 3.3 change included version-specific image behavior and the 3.4 change introduced an ApiVersion abstraction used across product, taxonomy, Robotoff, and Open Prices clients. Those SDK changes are not only about mobile developer ergonomics. They are a reminder that food-data clients need to know which contract they are speaking before they can safely interpret fields, validation rules, or related resources.

Thesis: recipe and nutrition APIs should model nutrient units as versioned, scoped validation contracts, because unit changes affect performance, correctness, and client compatibility in ways that cannot be repaired by a generic amount + unit pair.

What existing recipe API posts have already covered

Recent Recipe API posts have repeatedly argued for explicit data contracts: ingredient form states, locale fallback safety, role annotations, hazard taxonomies, optional AI dependencies, extensible metadata, label grammar rules, paginated contribution APIs, versioned clients, and reviewed taxonomy changes. The repeated angle to avoid is “a fresh Open Food Facts taxonomy or infrastructure change proves recipe APIs need a more explicit schema.”

This post uses a different angle. It is not about naming another ingredient category or adding another metadata extension. It focuses on what happens when a valid user action changes the unit semantics of nutrition data already on screen, already cached, or already being consumed by typed clients. The interesting product question is not “should units be stored?” It is “how narrowly can a nutrition API validate, recompute, and publish unit-dependent values without hiding important cross-field constraints?”

Source map

Fresh primary evidence:

  • Open Food Facts server commit, August 3, 2026: a fix for nutrient-unit changes causing a product edit UI freeze. It explains the root cause, the redundant validation fan-out, and the nutrition dependencies that must still be preserved.
  • Open Food Facts Dart SDK commit, July 27, 2026: API 3.4 support with a new ApiVersion abstraction across multiple clients.
  • Open Food Facts Dart SDK commit, July 27, 2026: API 3.3 support, including version-specific handling for image methods and response formats.

Older background context:

  • Open Food Facts public API and SDK ecosystem, as a general example of food products, nutrition, taxonomies, images, and price data being consumed through multiple clients and contribution flows.
  • Common nutrition data practice: products and recipes may expose per-100g, per-serving, and prepared-state values, while apps transform them into meal, portion, or shopping-list calculations.

The new information surplus is the connection between validation fan-out and API design: which fields are causally related, which validations must be recomputed together, and which clients need versioned behavior.

Why nutrient units are harder than they look

Most recipe APIs start with something like this:

{
  "nutrients": {
    "sodium": { "amount": 480, "unit": "mg" },
    "fat": { "amount": 12, "unit": "g" },
    "saturatedFat": { "amount": 4, "unit": "g" }
  }
}

That is useful, but it is not enough for a product that supports editing, ingestion, personalization, or third-party clients. The missing contract is not just conversion math. It includes:

  • the basis of the value: per 100g, per serving, per recipe, per prepared serving, or per package;
  • the preparation state: as sold, cooked, drained, diluted, reconstituted, or otherwise prepared;
  • the validation group: which nutrients must be checked together;
  • the evidence source: label OCR, manual contributor entry, manufacturer feed, calculated recipe total, or inferred value;
  • the API version: which shape and validation semantics the client expects;
  • the recomputation scope: what should be recalculated when only one unit selector changes.

Without those fields, a seemingly harmless unit edit can produce three classes of failure.

First, correctness failures. If saturated fat and total fat use different unit assumptions, the API may miss an impossible value or flag a valid value incorrectly. If sugars and carbohydrates are not validated on the same basis, a “sugars greater than carbohydrates” warning becomes noisy.

Second, performance failures. The Open Food Facts UI freeze is a concrete example: a broad event handler revalidated every nutrient value when a narrower dependency graph would have been sufficient. On a recipe platform, the same pattern can move from browser freezes to API latency, background job storms, or cache invalidations that affect many users.

Third, compatibility failures. A web editor, mobile SDK, meal-planning backend, and partner grocery integration may not all upgrade at the same time. If validation behavior changes without version negotiation, one client may submit data that another client interprets differently.

A better model: validation groups, bases, and versions

A practical nutrition API should separate the nutrient measurement from the validation contract that governs it. One possible sketch:

{
  "nutritionProfile": {
    "id": "np_123",
    "apiVersion": "2026-08-03",
    "basis": "per_serving",
    "serving": {
      "quantity": 1,
      "unit": "bowl",
      "gramWeight": 340
    },
    "preparationState": "prepared",
    "nutrients": [
      {
        "id": "fat",
        "label": "Fat",
        "amount": 12,
        "unit": "g",
        "source": "label",
        "validationGroups": ["fat_balance"]
      },
      {
        "id": "saturated_fat",
        "label": "Saturated fat",
        "amount": 4,
        "unit": "g",
        "source": "label",
        "validationGroups": ["fat_balance"]
      },
      {
        "id": "carbohydrates",
        "label": "Carbohydrates",
        "amount": 38,
        "unit": "g",
        "source": "calculated",
        "validationGroups": ["carbohydrate_balance"]
      },
      {
        "id": "sugars",
        "label": "Sugars",
        "amount": 9,
        "unit": "g",
        "source": "calculated",
        "validationGroups": ["carbohydrate_balance"]
      }
    ],
    "validationPolicy": {
      "id": "nutrition_validation_2026_08",
      "revalidateOnUnitChange": "affected_group",
      "warnings": [
        {
          "code": "SATURATED_FAT_GT_FAT",
          "group": "fat_balance",
          "severity": "warning"
        },
        {
          "code": "SUGARS_GT_CARBOHYDRATES",
          "group": "carbohydrate_balance",
          "severity": "warning"
        }
      ]
    }
  }
}

The important part is the separation of concerns. Nutrient values are data. Validation groups describe dependencies. The policy describes how to recompute. The API version tells clients which behavior they can rely on.

Decision framework for API builders

Decision Simple approach Contract-first approach When the contract matters
Unit storage Store amount and unit Store amount, unit, basis, preparation state, and source Any calculated nutrition, prepared foods, or serving conversions
Validation Revalidate the whole object Revalidate affected dependency groups Large forms, batch imports, partner feeds, or latency-sensitive editors
Cross-field rules Hard-code warnings in UI Publish versioned validation policies Multiple clients or SDKs submit nutrition data
Client compatibility Assume latest behavior Expose API/version capability negotiation Mobile SDKs, partners, or embedded clients upgrade slowly
Errors Return generic invalid field messages Return rule codes, affected fields, and normalized comparison basis Developer-facing APIs and automated correction workflows

API design consequences

Expose unit changes as patch operations, not blind replacement. A PATCH endpoint can distinguish “the user changed sodium from mg to g” from “the recipe was recalculated from a new ingredient list.” That distinction lets the server revalidate only affected groups and return targeted warnings.

Return dependency-aware validation responses. Instead of:

{ "error": "Invalid nutrition" }

return:

{
  "status": "warning",
  "validationPolicy": "nutrition_validation_2026_08",
  "checks": [
    {
      "code": "SUGARS_GT_CARBOHYDRATES",
      "severity": "warning",
      "basis": "per_serving",
      "affectedNutrients": ["sugars", "carbohydrates"],
      "message": "Sugars cannot exceed total carbohydrates on the same basis."
    }
  ]
}

This response is better for developers because it is actionable. It is better for operators because warnings can be counted by rule code. It is better for AI-assisted correction because an agent can see which fields are related instead of guessing from labels.

Version nutrition semantics separately from endpoint paths. The recent Dart SDK changes show the value of explicit API versions across clients. Nutrition validation may evolve faster than the endpoint URL. A platform might add a new warning for added sugars, introduce prepared-state conversion, or change rounding behavior. Those changes should be discoverable through a capability or policy endpoint, not buried in release notes.

For example:

GET /v1/nutrition/validation-policies/current
GET /v1/nutrition/validation-policies/nutrition_validation_2026_08

That pattern lets clients cache policy metadata, display localized warnings, and decide whether an old SDK can safely submit edits.

Edge cases that should shape the contract

Prepared versus as-sold values. Dry pasta, drink mixes, soup concentrates, and pancake batter all change nutrient basis after preparation. A unit change on the prepared row should not necessarily invalidate the as-sold row, but the UI may need to compare both when showing consumer-facing nutrition.

Serving size ambiguity. “One cup,” “one slice,” and “one package” are not stable unless tied to gram weight or package state. If gram weight is missing, conversion should be marked as unavailable rather than silently estimated.

Trace and zero values. A label may show 0g due to rounding while a calculated recipe total contains a small non-zero amount. APIs should distinguish declared zero, rounded zero, below-threshold trace, and unknown.

International units and localization. Some regions display energy in kcal, others in kJ, and labels vary in which nutrients are mandatory. Localized labels should not change canonical nutrient identifiers or validation group membership.

Implementation checklist

Before shipping nutrition editing or calculated recipe nutrition, ask:

  • Do we store the value basis separately from the displayed serving text?
  • Can we represent as-sold and prepared nutrition without overloading one field?
  • Which nutrient pairs or groups have cross-field rules?
  • Does a unit change trigger whole-object validation or affected-group validation?
  • Are validation policies versioned and inspectable by SDKs?
  • Do errors include rule codes, affected fields, comparison basis, and severity?
  • Can old clients discover that a newer nutrition contract exists?
  • Can we accept draft nutrition data without indexing it as authoritative?
  • Do caches and search indexes know which derived values depend on a changed unit?
  • Can operators measure validation warnings by source, client version, and nutrient group?

If the answer to most is no, the feature may work in demos but become brittle under contribution flows, mobile clients, AI extraction, and grocery integrations.

Where Recipe API fits

For Recipe API users, the practical takeaway is to evaluate nutrition endpoints as contracts, not just coverage tables. Ask how values are scoped, how serving and preparation are represented, how validation rules are exposed, and whether clients can safely evolve over time.

The recent Open Food Facts changes expose the operational reality behind nutrition data: unit handling is not a dropdown. It is a dependency graph. Once recipe products treat it that way, they can build faster editors, safer personalization, more reliable imports, and SDKs that do not break when nutrition semantics improve.

Sources

Start Building

One consistent schema on every response. Get a free key and ship in minutes.