curl --request GET \
--url https://api.example.com/api/stats/nutrients/{product_id} \
--header 'Authorization: <authorization>'{
"item": "<string>",
"nutrients": {
"energy_kcal": 123,
"carbohydrate": 123,
"protein": 123,
"fiber": 123,
"total_sugars": 123,
"saturated_fat": 123,
"vitamin_a": 123,
"vitamin_c": 123,
"potassium": 123,
"iron": 123,
"calcium": 123,
"sodium": 123,
"cholesterol": 123
}
}Retrieve detailed nutrition information for a specific product, optimized for radar chart visualization
curl --request GET \
--url https://api.example.com/api/stats/nutrients/{product_id} \
--header 'Authorization: <authorization>'{
"item": "<string>",
"nutrients": {
"energy_kcal": 123,
"carbohydrate": 123,
"protein": 123,
"fiber": 123,
"total_sugars": 123,
"saturated_fat": 123,
"vitamin_a": 123,
"vitamin_c": 123,
"potassium": 123,
"iron": 123,
"calcium": 123,
"sodium": 123,
"cholesterol": 123
}
}Bearer YOUR_ACCESS_TOKENShow Available nutrients
{
"item": "Whole Milk",
"nutrients": {
"energy_kcal": 61.0,
"carbohydrate": 4.8,
"protein": 3.2,
"fiber": 0.0,
"total_sugars": 5.1,
"saturated_fat": 1.9,
"vitamin_a": 46.0,
"vitamin_c": 0.0,
"potassium": 143.0,
"iron": 0.03,
"calcium": 113.0,
"sodium": 43.0,
"cholesterol": 10.0
}
}
{
"item": "Product not found or no nutrition data",
"nutrients": {}
}
{
"detail": "Authorization header missing or invalid."
}
curl -X GET "https://api.expireeye.com/api/stats/nutrients/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
const productId = "550e8400-e29b-41d4-a716-446655440000";
const accessToken = "your-jwt-token";
fetch(`https://api.expireeye.com/api/stats/nutrients/${productId}`, {
method: "GET",
headers: {
"Authorization": `Bearer ${accessToken}`,
},
})
.then(response => response.json())
.then(data => {
if (Object.keys(data.nutrients).length === 0) {
console.log("No nutrition data available");
return;
}
console.log(`Nutrition for: ${data.item}`);
Object.entries(data.nutrients).forEach(([key, value]) => {
const label = key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
console.log(`${label}: ${value}`);
});
})
.catch(error => console.error("Error:", error));
import { useEffect, useState } from 'react';
import { Radar } from 'react-chartjs-2';
import axios from 'axios';
import {
Chart as ChartJS,
RadialLinearScale,
PointElement,
LineElement,
Filler,
Tooltip,
Legend,
} from 'chart.js';
ChartJS.register(
RadialLinearScale,
PointElement,
LineElement,
Filler,
Tooltip,
Legend
);
interface NutrientsResponse {
item: string;
nutrients: Record<string, number>;
}
function NutrientRadarChart({
productId,
accessToken,
}: {
productId: string;
accessToken: string;
}) {
const [data, setData] = useState<NutrientsResponse | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
axios
.get<NutrientsResponse>(
`https://api.expireeye.com/api/stats/nutrients/${productId}`,
{
headers: { Authorization: `Bearer ${accessToken}` },
}
)
.then(response => {
setData(response.data);
})
.finally(() => setLoading(false));
}, [productId, accessToken]);
if (loading) return <div>Loading nutrition data...</div>;
if (!data || Object.keys(data.nutrients).length === 0) {
return <div>No nutrition data available for this product</div>;
}
// Format labels (e.g., "energy_kcal" -> "Energy (kcal)")
const formatLabel = (key: string): string => {
const units: Record<string, string> = {
energy_kcal: 'Energy (kcal)',
carbohydrate: 'Carbs (g)',
protein: 'Protein (g)',
fiber: 'Fiber (g)',
total_sugars: 'Sugars (g)',
saturated_fat: 'Sat. Fat (g)',
vitamin_a: 'Vit. A (μg)',
vitamin_c: 'Vit. C (mg)',
potassium: 'Potassium (mg)',
iron: 'Iron (mg)',
calcium: 'Calcium (mg)',
sodium: 'Sodium (mg)',
cholesterol: 'Cholesterol (mg)',
};
return units[key] || key;
};
const labels = Object.keys(data.nutrients).map(formatLabel);
const values = Object.values(data.nutrients);
const chartData = {
labels,
datasets: [
{
label: data.item,
data: values,
backgroundColor: 'rgba(16, 185, 129, 0.2)',
borderColor: 'rgba(16, 185, 129, 1)',
borderWidth: 2,
pointBackgroundColor: 'rgba(16, 185, 129, 1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(16, 185, 129, 1)',
},
],
};
const options = {
responsive: true,
plugins: {
legend: {
position: 'top' as const,
},
title: {
display: true,
text: `Nutrition Profile: ${data.item}`,
font: {
size: 18,
},
},
},
scales: {
r: {
beginAtZero: true,
ticks: {
stepSize: 20,
},
},
},
};
return (
<div>
<Radar data={chartData} options={options} />
{/* Nutrition Facts Table */}
<div className="mt-6">
<h4 className="font-semibold mb-2">Nutrition Facts</h4>
<table className="w-full border-collapse">
<thead>
<tr className="bg-gray-100">
<th className="border p-2 text-left">Nutrient</th>
<th className="border p-2 text-right">Amount</th>
</tr>
</thead>
<tbody>
{Object.entries(data.nutrients).map(([key, value]) => (
<tr key={key} className="hover:bg-gray-50">
<td className="border p-2">{formatLabel(key)}</td>
<td className="border p-2 text-right font-mono">
{value.toFixed(2)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
id, addedAt)nutritionId linking to the Nutrition table