Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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.

David Yun

April 25, 2014
Tweet

More Decks by David Yun

Other Decks in Programming

Transcript

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

    template… Automatic generated, updated, understandable… Fast, as small as we requested…
  2. 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
  3. 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
  4. 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
  5. Debate on page vs. offset } { page: 2, per_page:

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

    per_page: 2 } 頭推 :) 超好吃 :) 不好吃阿!:( 樓上決⾾鬥阿!:@ ڂࠃ੉qຝṽὗ 1 2 3 4 5 ڂࠃ੉qຝṽὗ { offset:1, limit: 2 }
  7. - Given: RSpec and TravisCI! - Requirement:! - light-weight! -

    generate docs based on specs! - generate docs in html format! - We found square/fdoc Documentation
  8. 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
  9. 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
  10. - Everything is fine, but…! - Scaffold errors will overwrite

    with an empty fdoc! - Can’t allow true/false spec cases.! - zipmark/rspec_api_documentation Documentation
  11. 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
  12. 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
  13. 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" } } ] }
  14. 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" } } ] }
  15. # 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
  16. 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 } ] }
  17. 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.
  18. Secure - Use User-Agent header! - Constrain your routes, use

    “only” and “except”! - kickstarter/rack-attack! - Rack middleware for blocking & throttling
  19. – 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.”
  20. “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