關於營養的 API 是我一直想做的主題,因為我一直都有想要來自己寫個食譜之類的 APP 的想法。在看了幾個不同的 Nutrition 類別 API 之後,選了 Nutritionix API 來玩看看。
首先先去註冊了 API Key,然後在 Dashboard > View API Keys 裡面找到了 APP ID 跟 APP Key。
現在來看看文件。
裡面有三個 Endpoint, 分別是
第一個跟第三個的 Natural Language 是讓使用者可以以語意化的方式搜尋營養成分跟運動耗能的資料,
比方說可以直接輸入 “run for 30 mins” 這種句子來得到資料。
第二個則是給出食物名稱直接抓取資料。
然後,點進去的時候,我有些傻眼。因為他的文件,居然都寫在 Google Doc 上…..
從這個陽春的 Google Doc 文件上面會連到一個 API endpoint 列表:
我先看了 /v2/natural/nutrients
的部分,基本上他的用法是在 Request Body 裡面放上一個 query 物件,裡面寫著語意化的句子比方說「我吃了一顆蘋果」然後返還一個 JSON。
不知道為什麼但這個我一直沒試成功。
後來試了 /v2/search/instant/
直接喂關鍵字,會拿到含有關鍵字的搜尋結果。
現在就來試寫看看。
一樣先建立一個簡單的 XHR 物件:
var apiKey = "4b314d3e9171fbe99c6cdf16127ba93e";
var apiId = "4c143c55";
var queryItem = "apple";
var url = "https://trackapi.nutritionix.com/v2/search/instant?query=";
function makeRequest(queryItem) {
xhr = new XMLHttpRequest();
xhr.onload = function() {
var response = JSON.parse(this.responseText);
console.log(response);
};
xhr.open("GET", url + queryItem, true);
// App ID 跟 App Key 放這邊
xhr.setRequestHeader("x-app-id", apiId);
xhr.setRequestHeader("x-app-key", apiKey);
xhr.send();
}
makeRequest();
與之前都不同的地方是,Nutritionix 的 App ID 跟 App Key 不能用 URL 參數的方式被放進去,而是必須放在 Request Header 裡。
送出這個簡單的 Request 之後可以得到這樣的 Response:
Response 裡面有兩個 Property,一個是 branded
就是有牌的加工食物、另一個就是一般食物 common
,每個 Propety 裡面都是一個含有 20 個物件的 array 。
那既然成功的拿到了這些資料,接下來就是跟前兩天一樣,把拿到的東西放進 HTML 裡面。
先在 HTML 裡面放個搜尋框框還有結果區塊:
<form action="search">
<label>Search</label>
<input type="text">
<input type="submit">
</form>
<div class="result">
<h3>Result</h3>
</div>
然後分別把它們在 Javascript 裡面選取起來:
var form = document.querySelector('form');
var input = document.querySelector('input[type="text"]');
var result = document.querySelector('.result');
現在畫面是這樣的:
接下來抓取使用者輸入框框裡的文字放進 queryItem
裡,把 submit
event 綁上 <form>
。
API Request 會在按下 Search 的時候發生,在其中傳入 queryItem
的參數。
function search(e){
e.preventDefault();
queryItem = input.value;
makeRequest(queryItem);
input.value= "";
}
form.addEventListener('submit', search);
寫一個在 HTML 裡插入 container 的 function:
function createFood(name, qty, unit, photo){
var item = document.createElement('div');
var foodName = document.createElement('h4');
var serving = document.createElement('p');
var img = document.createElement('img');
item.classList.add('item');
foodName.innerHTML = name;
serving.innerHTML = qty+' '+unit;
img.src = photo;
result.appendChild(item);
item.appendChild(img);
item.appendChild(foodName);
item.appendChild(serving)
}
這樣一來,在 xhr.onload
裡拿到 response
的時候,就可以用 .map()
的方法,把裡面每個物件綁上createFood
這個 function 然後在畫面上 render 出來:
xhr.onload = function() {
var response = JSON.parse(this.responseText);
response.common.map(function(food){
createFood(food.food_name,
food.serving_qty,
food.serving_unit,
food.photo.thumb
)
})
};
以下是完整 JS Code:
var apiKey = "4b314d3e9171fbe99c6cdf16127ba93e";
var apiId = "4c143c55";
var queryItem;
var url = 'https://trackapi.nutritionix.com/v2/search/instant?query=';
var form = document.querySelector('form');
var input = document.querySelector('input[type="text"]');
var result = document.querySelector('.result');
function search(e){
e.preventDefault();
queryItem = input.value;
makeRequest(queryItem);
input.value= "";
}
function createFood(name, qty, unit, photo){
var item = document.createElement('div');
var foodName = document.createElement('h4');
var serving = document.createElement('p');
var img = document.createElement('img');
item.classList.add('item');
foodName.innerHTML = name;
serving.innerHTML = qty+' '+unit;
img.src = photo;
result.appendChild(item);
item.appendChild(img);
item.appendChild(foodName);
item.appendChild(serving)
}
function makeRequest(queryItem) {
xhr = new XMLHttpRequest();
xhr.onload = function() {
var response = JSON.parse(this.responseText);
response.common.map(function(food){
createFood(food.food_name,
food.serving_qty,
food.serving_unit,
food.photo.thumb
)
})
};
xhr.open(
"GET",
url+queryItem,
true
);
xhr.setRequestHeader('x-app-id',apiId);
xhr.setRequestHeader('x-app-key',apiKey);
xhr.send();
}
form.addEventListener('submit', search)
還加上了一點點 CSS:
.result{
display:flex;
flex-wrap:wrap;
}
.item{
min-width:200px;
margin:20px auto;
}
完成。
後記:
在我快寫完的時候,我才發現此 API 只有有牌(branded)的食物有卡路里資料......
然後他缺圖又好多,所以之後如果需要營養資料像是抓卡路里等,不考慮使用此 API。