• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
10 Things to Make API Users Like You
 

10 Things to Make API Users Like You

on

  • 275 views

Having experiences of developing iCook API, I'd like to share some gems, skills and tips to build an efficient, consistent and well-documented API with Ruby on Rails which makes API users like you ...

Having experiences of developing iCook API, I'd like to share some gems, skills and tips to build an efficient, consistent and well-documented API with Ruby on Rails which makes API users like you more.

Statistics

Views

Total Views
275
Views on SlideShare
265
Embed Views
10

Actions

Likes
3
Downloads
3
Comments
0

2 Embeds 10

http://www.slideee.com 7
https://www.linkedin.com 3

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    10 Things to Make API Users Like You 10 Things to Make API Users Like You Presentation Transcript

    • 10 Things to Make API Users Like You @abookyun - 我是 David。今天分享的主題是讓 API 使用者喜歡你的 10 件事情。
    • iCook 愛料理 The largest recipes sharing website in Taiwan. - 2012 年加入 Polydice,是 Backend Engineer - iCook 是 Polydice 的主要專案,是台灣最大的食譜分享社群網站。 - iCook 是用 Ruby on Rails 設計開發的 - 每個月超過兩百萬人次在上面分享與尋找自己喜歡的食譜。
    • iCook 愛料理 We’re ready for! iOS, Android and Windows - iCook 目前在各大平台上都已經有 app,近期會有 Windows 8 app - app 與 server 透過 API 溝通,所以今天就是分享在開發 API 的經驗
    • 40,000 recipes 600,000 members 1,000,000 downloads 1,600,000 API calls per day - 相關數據,其中 API calls 約為 160 萬次每日的量
    • Principles
    • Principles Documentation Consistent Efficient From routes, request & response data, template… Automatic generated, updated, understandable… Fast, as small as we requested… - 設計 API 的有三個重要原則分別是:C、D、E - 一致性包含 routes 的規則、request & response 的資料內容一致性 - 一份完整易懂與隨時更新的文件對 API Users 是重要的敲門磚 - 高效率的 API 擁有很低的 response time、最少的 requests 等特性 - 接下來的幾個案例分享都會圍繞在這幾個重要原則上
    • Diary 料理⽇日記 - Diary 料理日記是近期的新產品,隨手做的料理輕鬆拍照上傳分享 - 接下來的案例會用此 API 做說明。料理 = dish、留言 = comment、使用者 = user
    • RESTful and Reasonable routes - RESTful 是近年網站開發的重要哲學 - Rails 也是以這樣的哲學做出來的 Framework,已內建 RESTful - routes 除了 RESTful 以外,Reasonable 也是很重要的
    • RESTful and Reasonable routes # app/models/comment.rb class Comment < ActiveRecord::Base belongs_to :commentable, polymorphic: true end ! # routes.rb resources :dishes do resources :comments, to: "dishes/comments" end ! # GET /dishes/1/comments # POST /dishes/1/comments # DELETE /dishes/1/comments/1 - 一般的 dishes has_many comments 的 routes 的寫法沒問題 - 但考慮到 comments 是 polymorphic type,也就是說他可以是料理的留言,也可以是食譜的留言。在 DELETE 留言時其實就不需要特別強調他是料理的 或是食譜的留言,只要給 ID 就可以刪除了
    • RESTful and Reasonable routes # routes.rb resources :dishes do resources :comments, to: “dishes/comments" , except: [:destroy] end resources :comments, only: [:destroy] ! # GET /dishes/1/comments # POST /dishes/1/comments # DELETE /comments/1 - 更換後的 routes,對 API Users 來說更簡單容易理解。
    • Implicit in routes - RESTful 的一個重要關鍵就是他暗示了所有 route 都是一個 resource - 這樣的暗示就是一種 API 開發者與 Users 之間的共識,需要更精確的暗示 API Users,來保持 API 的一致性
    • Implicit in routes resources :users do resources :settings, to: "users/settings" end ! # GET /users/username/settings # PUT /users/username/settings ! resources :settings ! # GET /settings # PUT /settings - 上方的 routes 其實會有暗示可以帶入別人的 username 來查詢/修改設定的可能,應該要被修改成下方的形式
    • Debate on page vs. offset - 曾經與 API Users 討論在翻頁的 API 應該要用哪一套 - 大部分情況下這只是習慣上的選擇,但以下有個 edge case
    • Debate on page vs. offset }{ page: 2, per_page: 2 }! { offset: 2, limit: 2 } 頭推 :) 超好吃 :) 不好吃阿!:( 樓上決⾾鬥阿!:@ 떡볶이~敲好吃 1 2 3 4 5 떡볶이~敲好吃 - 料理有五個留言,一般讀取兩者都沒有差別
    • Debate on page vs. offset }}{ page: 2, per_page: 2 } 頭推 :) 超好吃 :) 不好吃阿!:( 樓上決⾾鬥阿!:@ 떡볶이~敲好吃 1 2 3 4 5 떡볶이~敲好吃 { offset:1, limit: 2 } - 但是當使用者看完第一頁之後刪除第一個再換第二頁 - local 會知道 objects 剩下一個,offset = 1,顯示上沒有問題 - 但是 page/per_page 就會出現不知道四樓想要跟誰決鬥的狀況 - 這是可以透過 app 的設計解決的問題 - page/per_page 由 Kaminari 或 will_paginate 這兩個知名的 gem 來做相當簡單,limit/offset 則是 API Users 端會多一些邏輯判斷 - 這其實只會發生在「連續」的資料上 - 最好當然是兩者都能提供,像是 Facebook, Twitter
    • Documentation - 一份完整容易閱讀的文件當然對 API Users 是相當重要的敲門磚 - 接下來分享我們用什麼方式處理文件的問題
    • - Given: RSpec and TravisCI! - Requirement:! - light-weight! - generate docs based on specs! - generate docs in html format! - We found square/fdoc Documentation - 我們公司的開發流程 - 所以我們需要:輕量、根據 specs 產生文件、能自動產生可閱讀格式 - 表示我們不需要額外增加開發的成本 - API Users 也可以得到確認通過測試版本程式的文件 - 也不會有文件有,卻不能用的 endpoint - 選用 square 的 fdoc
    • Documentation # spec/controllers/members require 'fdoc/spec_watcher' ! describe MembersController do include Fdoc::SpecWatcher context '#show', fdoc: 'members/list' do # ... end end ! FDOC_SCAFFOLD=true bundle exec rspec spec - 不用更改原本 Spec 寫法 - 透過內建的指令產生 YAML(呀磨) 檔案
    • Documentation # docs/fdoc/members/list-GET.fdoc description: The list of members. requestParameters: properties: limit: type: integer required: no default: 50 description: Limits the number of results returned. responseParameters: properties: members: type: array items: title: member description: Representation of a member type: object properties: name: description: Member's name type: string required: yes example: Captain Smellypants responseCodes: - status: 200 OK successful: yes description: A list of current members - status: 400 Bad Request successful: no description: Indicates malformed parameters - 分成 request、response、responseCodes 等區塊
    • Documentation > fdoc convert spec --output=./html - 一樣透過內建指令產生 html,而這就是根據 .fdoc 產生 - 有 endpoint、request 應該要有的欄位、格式、必須欄位等等
    • - Everything is fine, but…! - Scaffold errors will overwrite with an empty fdoc! - Can’t allow true/false spec cases.! - zipmark/rspec_api_documentation Documentation - 在產生 fdoc 的指令遇到錯誤會覆寫成空白 fdoc 的問題,所以現在我們自己複製貼上 .fdoc,比較少出錯 - 不能寫正反兩面測試。例如有帶 token 與沒帶 token 的 user 測試不能 - 目前有在 survey 另外一套 gem
    • Documentation - 亮點一:Body 有 request 的「結構」 - 亮點二:cURL 有可直接使用的範例,相當直覺 - 但是,有另外的 DSL,這樣表示我們要重寫全部 specs
    • jbuilder & serializer - rails 知名的 template 使用心得 - 是 active_model_serializer(名稱太長沒有用)
    • jbuilder & serializer # app/models/recipe.rb class Dish < ActiveRecord::Base belongs_to :user end ! # app/models/user.rb class User < ActiveRecord::Base has_many :dishes end ! # app/controllers/dishes_controller.rb class DishesController < ApplicationController def index @dishes = Dish.all end end - 簡單的 model, controller
    • jbuilder & serializer # app/views/dishes/index.json.jbuilder json.dishes dishes do |json, dish| json.id dish.id json.description dish.description json.url dish_url(dish) json.partial! "api/v1/users/user", user: dish.user end ! # app/serializers/dish_serializer.rb class DishSerializer < ActiveModel::Serializer attributes :id, :description, :url has_one :user ! def url dish_url end end - jbuilder 可以自由組裝 key, value - jbuilder 也有 partial - jbuilder 每個 action 要有一個 *.json.jbuilder - serializer 如同他的名字是 model 的 serializer,一個 model 定義一次 - serializer 比較物件導向(Object Oriented) - serializer 一致性相當高,不需要重複定義,反之 jbuilder
    • jbuilder & serializer # dishes/index.json { dishes: [ { id: 1, description: "dish1", url: "dishes/1", user: { username: "user1" } }, { id: 2, description: "dish2", url: "dishes/2", user: { username: "user1" } } ] } - 當然兩個可以產生一樣的結果
    • jbuilder & serializer - jbuilder 等同於樂高積木 - serializer 等同於俄羅斯娃娃 - API 設計是取捨的問題
    • Duplicate Data - jbuilder & serializer 有個延伸問題就是重複的資料
    • Duplicate Data # dishes/index.json { dishes: [ { id: 1, description: "dish1", url: "dishes/1", user: { username: "user1" } }, { id: 2, description: "dish2", url: "dishes/2", user: { username: "user1" } } ] } - 同一個人的兩道不同料理的資料內容 - 資料可大可小,不巧的話會造成多餘的浪費
    • # app/serializers/base_serializer.rb class BaseSerializer < ActiveModel::Serializer # sideload related data by default embed :ids, include: true end ! # app/serializers/dish_serializer.rb class DishSerializer < BaseSerializer attributes :id, :description, :url has_one :user ! def url dish_url end end Duplicate Data - jbuilder 可自由拆裝就不多說明 - serializer 可以透過定義 superclass,定義 embed 的方法,預設 include 物件的 associations 進來
    • Duplicate Data # dishes/index.json { users: [ { "id": 1, username: "user1" } ], dishes: [ { id: 1, description: "dish1", url: "dishes/1", user_id: 1 }, { id: 2, description: "dish2", url: "dishes/2", user_id: 1 } ] } - users 的陣列是自動產生的,內容是這次 request 中所有用到的 user - dishes 內原本是 user 的地方則變成 user_id,內容只有 id - 節省很多空間,但是會造成 API Users 可能需要 parse 兩次 - API 設計終究是取捨的問題
    • rack-rewrite - API 開發時程長了之後可能會有許多 legacy routes - 一般來說會在 routes.rb 裡面 redirect,但可以有更有效率的方法 - gem: rack-rewrite
    • rack-rewrite A rack middleware for defining and applying rewrite rules. Rewrite - rewrite 是 web server, e.g. Apache, nginx(engine x) 的 term,也就是 redirect 的意思 - 他是一個 rack middleware,所以可以提前處理要轉向的 request,不用進到 rails,可以省掉很多時間,像是 redirect 不需要驗證身分就是很好的例子
    • rack-rewrite # Rewrite legacy routes config.middleware.insert_before(Rack::Runtime, Rack::Rewrite) do r301 '/mobile', 'http://mobile.icook.tw' r301 %r{/recipes?/.+?/dishes/(.*)}, '/dishes/$1' end ! # Better maintainability than nginx rewrite and # better performance than rails routes. - 一般的 route 轉向,支援 regular expression(ruby) - 所以總體來說他比 nginx 的 rewrite 規則好寫也好懂,效率也比到 rails 看到 routes 還要好很多
    • status code only, if it’s suitable - 這是一個通則,就是盡量只傳必要的資料就好 - 而如果目前已經有豐富定義的 HTTP status code 能夠解決的事情就這樣吧 - 目前最好的例子就是 CREATE 跟 DELETE 的 method
    • Secure - 一般來說 API Users 不需要知道這些,但還是提一下
    • Secure - Use User-Agent header! - Constrain your routes, use “only” and “except”! - kickstarter/rack-attack! - Rack middleware for blocking & throttling - API 開發者可以透過 User-Agent 來限制使用者存取,避免 Server 太容易被莫名的 request 攻擊(DDos) - 盡量限制開放有用到的 routes,不要冒險 - kickstarter/rack-attack 這個 gem 是 rack middleware,可以列黑白名單在 rails 之前就阻擋掉部分不需要的 request
    • – Neil Gaiman 2012 “People will tolerate how unpleasant you are if your work is good and you deliver it on time. ! ! People will forgive the lateness of your work if it is good and they like you. ! ! And you don’t have to be as good as everyone else if you’re on time and it’s always a pleasure to hear from you.” - 最後引用一段我滿喜歡的演講,來自於 Neil Gaiman 2012 在 University of Art 的演講
    • “People will tolerate the incomplete document if your API is efficient and consistent. ! People will forgive the inconsistency of your API if it is efficient and the document is fine. ! And your API doesn’t have to be as efficient as everyone else if it’s consistent and it’s always a pleasure to read the documents.” – David Yun 2014 - 改寫成 API 的三個原則也滿適用的。
    • Q & A