10 Things to Make API Users Like You.

10 Things to Make 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.

19c4a0b3d480fc14181f6818568fe32f?s=128

David Yun

April 25, 2014
Tweet

Transcript

  1. 10 Things to Make API Users Like You @abookyun

  2. iCook 愛料理 The largest recipes sharing website in Taiwan.

  3. iCook 愛料理 We’re ready for! iOS, Android and Windows

  4. 40,000 recipes 600,000 members 1,000,000 downloads 1,600,000 API calls per

    day
  5. Principles

  6. Principles Documentation Consistent Efficient From routes, request & response data,

    template… Automatic generated, updated, understandable… Fast, as small as we requested…
  7. Diary 料理⽇日記

  8. RESTful and Reasonable routes

  9. 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
  10. 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
  11. Implicit in routes

  12. 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
  13. Debate on page vs. offset

  14. Debate on page vs. offset } { page: 2, per_page:

    2 }! { offset: 2, limit: 2 } 頭推 :) 超好吃 :) 不好吃阿!:( 樓上決⾾鬥阿!:@ ڂࠃ੉qຝṽὗ 1 2 3 4 5 ڂࠃ੉qຝṽὗ
  15. Debate on page vs. offset } } { page: 2,

    per_page: 2 } 頭推 :) 超好吃 :) 不好吃阿!:( 樓上決⾾鬥阿!:@ ڂࠃ੉qຝṽὗ 1 2 3 4 5 ڂࠃ੉qຝṽὗ { offset:1, limit: 2 }
  16. Documentation

  17. - Given: RSpec and TravisCI! - Requirement:! - light-weight! -

    generate docs based on specs! - generate docs in html format! - We found square/fdoc Documentation
  18. 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
  19. 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
  20. Documentation > fdoc convert spec --output=./html

  21. - Everything is fine, but…! - Scaffold errors will overwrite

    with an empty fdoc! - Can’t allow true/false spec cases.! - zipmark/rspec_api_documentation Documentation
  22. Documentation

  23. jbuilder & serializer

  24. 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
  25. 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
  26. 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" } } ] }
  27. jbuilder & serializer

  28. Duplicate Data

  29. 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" } } ] }
  30. # 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
  31. 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 } ] }
  32. rack-rewrite

  33. rack-rewrite A rack middleware for defining and applying rewrite rules.

    Rewrite
  34. 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.
  35. status code only, if it’s suitable

  36. Secure

  37. Secure - Use User-Agent header! - Constrain your routes, use

    “only” and “except”! - kickstarter/rack-attack! - Rack middleware for blocking & throttling
  38. – 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.”
  39. “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
  40. Q & A