iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 11
1

Nutritionix API

關於營養的 API 是我一直想做的主題,因為我一直都有想要來自己寫個食譜之類的 APP 的想法。在看了幾個不同的 Nutrition 類別 API 之後,選了 Nutritionix API 來玩看看。

首先先去註冊了 API Key,然後在 Dashboard > View API Keys 裡面找到了 APP ID 跟 APP Key。

現在來看看文件。

裡面有三個 Endpoint, 分別是

  • Natural Language for Nutrients
  • Instant Endpoint
  • Natural Language for Exercise

第一個跟第三個的 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;
}

完成。

Codepen 連結

後記:
在我快寫完的時候,我才發現此 API 只有有牌(branded)的食物有卡路里資料......
然後他缺圖又好多,所以之後如果需要營養資料像是抓卡路里等,不考慮使用此 API。


上一篇
[30apis] Day 9 : Behance API + JSONP 用法
下一篇
[30apis] Day 11 : Tumblr API
系列文
我每天都接一個API30

尚未有邦友留言

立即登入留言