Saltar al contenido

Datos nutricionales

Noms guarda un número por nutriente, por cada 100 del alimento: por 100 gramos si es un sólido, por 100 mililitros si es una bebida. Aparte, te dice los tamaños en que la gente mide ese alimento. Cada cifra que muestras en pantalla es la multiplicación de esas dos cosas.

Esta página es esa multiplicación, de punta a punta.

La idea, en palabras simples

Digamos que pides una barrita. Vienen tres cosas con ella:

basis_unit    grams              → cada valor de abajo es por 100 g
PROTEIN       10 g               → 10 g de proteína en cada 100 g
una porción   1 barrita, 50 g    → una porción son 50 g

Léelo como una sola frase:

Hay 10 g de proteína en cada 100 g, y una barrita son 50 g.

Una barrita es la mitad de 100 g, así que tiene la mitad de la proteína: 5 g. Dos barritas, 10 g. Tres, 15 g. Media barrita, 2,5 g.

No existe un campo de "proteína por barrita" que buscar, y no hace falta que exista. Escalas el valor por 100 según la porción que te interese, y esa misma multiplicación te da cada nutriente en cualquier tamaño de porción. Así es como un puñado de números guardados reconstruye cualquier etiqueta nutricional que quieras imprimir.

La fórmula

basis_unit hace dos trabajos a la vez, y es el campo que más conviene mirar con calma. Te dice por cuánto están expresados los valores (grams o milliliters) y además nombra el campo de la porción por el que hay que dividir:

basis_unitLos valores son porDivide por el campo
"grams"100 ggrams de la porción
"milliliters"100 mlmilliliters de la porción

El campo que nombra nunca es null, así que esa búsqueda siempre resuelve.

Un sólido, en JSON

La misma barrita, tal como la devuelve la API, recortada aquí a los tres campos de los que trata esta página:

{
  "basis_unit": "grams",        // cada valor de abajo es por 100 g
  "nutrients": [
    { "id": "ENERGY", "name": "Energy", "value": "400", "unit": "kcal" },
    { "id": "PROTEIN", "name": "Protein", "value": "10", "unit": "g" }
  ],
  "serving_sizes": [
    {
      "unit": "each",
      "descriptor": "bar",
      "quantity": "1",            // una barrita...
      "grams": "50",              // ...y una barrita pesa 50 g
      "milliliters": null,        // sin volumen; no lo adivinamos
      "is_default": true
    }
  ]
}

basis_unit es "grams", así que divides por los grams de la porción, que son 50:

proteína por barrita = 10  × 50 / 100 =   5 g
energía  por barrita = 400 × 50 / 100 = 200 kcal

Una bebida, la misma forma

Para algo medido por volumen el método no cambia en nada: solo cambia qué campo lees:

{
  "basis_unit": "milliliters",  // cada valor de abajo es por 100 ml
  "nutrients": [
    { "id": "ENERGY", "name": "Energy", "value": "60", "unit": "kcal" },
    { "id": "PROTEIN", "name": "Protein", "value": "3", "unit": "g" }
  ],
  "serving_sizes": [
    {
      "unit": "ml",
      "descriptor": null,
      "quantity": "250",          // 250 ml...
      "grams": null,              // ...de peso no declarado; la densidad no la adivinamos
      "milliliters": "250",       // ...que son, claro, 250 ml
      "is_default": true
    }
  ]
}

basis_unit es "milliliters", así que divides por los milliliters de la porción:

proteína por botella = 3  × 250 / 100 =   7,5 g
energía  por botella = 60 × 250 / 100 = 150 kcal

Fíjate en que grams es null en esa porción. Convertir un volumen en un peso requiere la densidad del alimento, y eso no lo adivinamos, así que la dimensión sobre la que el alimento no está anclado es de mejor esfuerzo y puede faltar. La que basis_unit nombra nunca falta.

Una sola función para ambos casos

Como la unidad nombra el campo, nunca tienes que ramificar:

function amountPerServing(food, nutrient, serving) {
  const basis = Number(serving[food.basis_unit]); // "grams" o "milliliters"
  return (Number(nutrient.value) * basis) / 100;
}

const serving = food.serving_sizes.find((s) => s.is_default);
const protein = food.nutrients.find((n) => n.id === "PROTEIN");
amountPerServing(food, protein, serving); // 5

Cómo leer una porción

serving_sizes es una lista. Cada entrada es una forma en que la gente mide ese alimento: un peso, un volumen o una pieza contable.

CampoQué es
unitLa medida: g, ml, cup, tbsp, tsp, L, oz, fl_oz o each
quantityCuántas unit forman esta porción: 1 barrita, 250 ml
descriptorNombra la porción cuando la unidad sola no alcanza (bar, slice, chopped); null si no
gramsCuánto pesa esta porción, cuando se sabe
millilitersCuánto mide en volumen esta porción, cuando se sabe
is_defaultLa porción principal del alimento. Exactamente una por alimento

Lee la porción de estos campos, nunca del nombre del alimento. Si vas a mostrar una sola cifra, muestra la porción con is_default: true: es la porción de referencia que la propia fuente trata como una porción.

Dos cosas para tener presentes

Cada nutriente trae además su propia unit (g, mg, kcal, ...), y eso es independiente del basis_unit del alimento. basis_unit dice por cuánto está expresado el valor; nutrient.unit dice en qué está medido. Proteína en "10" con unit: "g" en un alimento con base grams se lee: 10 gramos de proteína por cada 100 gramos de alimento.

Una respuesta completa

Todo lo anterior, en una fila real del catálogo: las almendras tostadas con sal de Blue Diamond, con el arreglo de nutrientes tan largo como realmente es:

GET
curl -G https://api.noms.sh/v1/foods/TIgbNPnzCIjX \
  --data-urlencode "include=nutrients,serving_sizes" \
  -H "Authorization: Bearer $NOMS_KEY"
Response
200 OK
{
"data": {
"id": "TIgbNPnzCIjX",
"barcode": "00041570110645",
"name": "Roasted salted almonds",
"description": null,
"scientific_name": null,
"country_of_origin": null,
"ingredients_text": "Almonds, sunflower oil, sea salt.",
"is_foundational": false,
"basis_unit": "grams",por 100 gramos → divide por serving.grams
"brand_id": "XoluRyOx9o3C",
"food_group_id": "NUTS_AND_SEEDS",
"nutrients": [
{
"id": "ADDED_SUGARS",
"name": "Sugars, added",
"value": "0.0",
"unit": "g"
},
{
"id": "ALPHA_TOCOPHEROL",
"name": "Vitamin E (alpha-tocopherol)",
"value": "25.1625",
"unit": "mg"
},
{
"id": "BIOTIN",
"name": "Biotin",
"value": "43.214286",
"unit": "mcg"
},
{
"id": "CALCIUM",
"name": "Calcium, Ca",
"value": "285.714286",
"unit": "mg"
},
{
"id": "CARBOHYDRATE",
"name": "Carbohydrate, by difference",
"value": "17.857143",
"unit": "g"
},
{
"id": "CHOLESTEROL",
"name": "Cholesterol",
"value": "0.0",
"unit": "mg"
},
],
"serving_sizes": [
{
"unit": "g",
"descriptor": null,
"quantity": "28.0",
"grams": "28.0",una porción pesa 28 g
"milliliters": null,
"is_default": true
},
{
"unit": "each",
"descriptor": "nuts",
"quantity": "28.0",
"grams": "28.0",
"milliliters": null,
"is_default": false
}
]
}
}

Los mismos tres campos, la misma multiplicación: 21.428571 × 28 / 100, redondeado para mostrar, son los 6 g de proteína impresos en el envase. Y lo mismo vale para cada otra cifra de su tabla.

Próximos pasos