Daniel Doubrovkine / Art.sy
dblock@dblock.org @dblockdotorg
Solid API or Else …




http://www.youtube.com/watch?v=l9vYE7B1_PU
The Rails Way: M(V)C

config/routes.rb

resources :artists

app/controllers/artists_controller.rb

class ArtistsController < ApplicationController
  def index
    @artists = …
    # all kinds of stuff that serves views
    respond_to do |format|
      format.html { @artists }
      format.json { render json: @artists.as_json }
     end
  end
End
The Rails Way: MVC
app/views/artists/index.json.erb

-@artists.each do |artist|
 {
    'first_name': '<%= @artist.first_name.to_json %>',
    'last_name': '<%= @artist.last_name.to_json %>'
 }
Occupy Rails?
»   Where does the API start and end?
»   How are we going to build API v2 on top of v1?
»   Is API testing the same as controller testing?
»   How much discipline are we going to need to keep sanity?
»   How will deal with more difficult problems?
    Caching, authentication, authorization …
Modern Web Applications: NoRails
»   MVC UI
»   RESTful API
»   Storage
Grape
»   API DSL                            class API < Grape::API
                                         version „1'
    rack-based / middleware
    http://github.com/intridea/grape
                                        namespace :artist
                                          get “:id” do
                                            Artist.find(params[:id]).as_json
                                          end
                                        end

                                         namespace :artists do
                                           get “/” do
                                             Artist.all.as_json
                                           end
                                         end
                                       end
Documentation
»   Developers Have the Attention Span of a Fish *
    * when reading documentation


»   Written in Markdown
    http://code.dblock.org/rendering-markdown-documents-in-rails


»   Reference will be Generated
»   API Sandboxes
    https://github.com/mmcnierney14/API-Sandbox


»   API Explorer
    https://github.com/mmcnierney14/API-Sandbox
Testing an API
# spec/spec_helper.rb



RSpec.configure do |config|
  config.include RSpec::Rails::RequestExampleGroup,
    :type => :request,
    :example_group => {
      :file_path => /spec/api/
    }
end




                  See “Writing Tests” @ https://github.com/intridea/grape
Mocking is for Java Programmers
describe "artworks" do
      before(:each) do
            login_as Fabricate(:admin)
      end
      describe "GET /api/v1/artwork/:slug" do
        it "returns an unpublished artwork" do
            artwork = Fabricate(:artwork, published: false)
            get "/api/v1/artwork/#{artwork.slug}"
            response.status.should == 200
            response.body.at_json_path(“id”).should == artwork.slug # Pathy!
        end
      end
  end
end
Version 1 Births Version 2
 »   Include Api_v1
 »   Folder-Driven Development (FDD)
     api/api_v1/…




      module Api_v1                                     module Api_v2
        version 'v1„                                      version 'v2„
        module Api_v1_Me                                  module Api_v1_Me
        module Api_v1_Artworks                            module Api_v2_Artworks
        # ...                                             # ...
      end                                               end


See “Modularizing Grape API” @ http://code.dblock.org/modularizing-a-ror-grape-api-multiple-versions
Exceptions Abort Flow
      »     Don’t question yourself, raise a hand.
       rescue_from :all, :backtrace => true

          error_format :json

          rescue_from Mongoid::Errors::Validations do |e|
            rack_response({ :message => e.message,
             :detail => e.document.errors,
             :backtrace => e.backtrace }.to_json)
            end
          end



See “Grape: trapping all exceptions within the API” @ http://code.dblock.org/grape-trapping-all-exceptions-within-the-api
Authentication Methods
»     XApp: Exchange client ID for an XApp token
      api/v1/api_xapp_auth.rb


»     OAuth 2.0: Browser-Based Redirects
      controllers/oauth_controller.rb


»     XAuth: Exchange credentials for an OAuth token
      controllers/oauth_controller.rb


»     Forms Login to Website
      devise/warden via user.rb



    See “Grape: API Authentication w/ Devise” @ http://code.dblock.org/grape-api-authentication-w-devise
Authenticated Users
»   Unauthenticated Calls
»   Authorized Apps
»   Logged In Users, RBAC

                      def authenticated_user
                          authenticated
                          error!('Unauthorized', 401) unless current_user
                      end
Object Identity
»       Everything has an ID
    »     Internal ID: BSON ObjectId
    »     External ID: humanly-readable ID

»       ID is the same for all API consumers
»       API consumers know of a single ID
    »     When do I use a Slug?

    »     When do I use BSON ObjectId?
JSON Formats
»   ActiveRecord as_json passes options recursively
    :all – all fields visible to the object’s owner

    :public – all fields visible to a user with :read permissions

    :short – enough fields visible to a user with :read permissions, used within a collection

»   JSON data can be grown incrementally
POST and PUT
»   Validate Input Parameters in Models
    save(hashie)
    valid_hash_fields :first, :last
Authorization
»   Admins have :create, :read, :update, :delete on everything, also
    known as :manage

»   Partners have :manage on their partner data
    eg. partner location, get :all JSON

»   Users have :manage on their personal data
    eg. my collection, get :all JSON

»   Everyone has :read on public data
    eg. a published artwork, get :public JSON
Authorization Usage
»    Implemented w/ CanCan

     cannot :read, Artwork
     can :read, Artwork do |artwork|
       artwork.published
     end



    error!(„Unauthorized', 403) unless
       current_user.has_authorization_to?(:delete, artist)
Pagination
»   paginate(collection)
    »   :offset or :page
    »   :size




          Pagination Helper for Grape @ https://gist.github.com/1335242
Logging
»   Implemented as Rack Middleware

»   Logs API Calls
Caching
»   Implemented w/Rails Cache / Memcached
»   Key based on Class and Identity
    »   Cache Locally
    »   Invalidate Aggressively
Cache Busting
»   IE9




                See “IE9: Cache-Busting with Grape Middleware” @
          http://code.dblock.org/ie9-cache-busting-with-grape-middleware
Instrumentation
»   See API Stats in New Relic
    config/initializers/new_relic_agent_instrumentation_api.rb




                  See “New Relic: Performance Instrumentaiton w/ Grape” @
         http://code.dblock.org/new-relic-performance-instrumentation-with-grape-api
Performance
»   Trends
Next
»   Deep Data
»   Caching in JSON
»   Generated Documentation
How to design a good API and why it matters (Joshua Bloch)
http://www.youtube.com/watch?v=aAb7hSCtvGw




1. Do one thing well
2. API is a Language, names matter
3. Documentation matters
4. Minimize mutability
5. Don’t make the client do anything the API could do

Building RESTful APIs w/ Grape

  • 1.
    Daniel Doubrovkine /Art.sy dblock@dblock.org @dblockdotorg
  • 2.
    Solid API orElse … http://www.youtube.com/watch?v=l9vYE7B1_PU
  • 3.
    The Rails Way:M(V)C config/routes.rb resources :artists app/controllers/artists_controller.rb class ArtistsController < ApplicationController def index @artists = … # all kinds of stuff that serves views respond_to do |format| format.html { @artists } format.json { render json: @artists.as_json } end end End
  • 4.
    The Rails Way:MVC app/views/artists/index.json.erb -@artists.each do |artist| { 'first_name': '<%= @artist.first_name.to_json %>', 'last_name': '<%= @artist.last_name.to_json %>' }
  • 5.
    Occupy Rails? » Where does the API start and end? » How are we going to build API v2 on top of v1? » Is API testing the same as controller testing? » How much discipline are we going to need to keep sanity? » How will deal with more difficult problems? Caching, authentication, authorization …
  • 6.
    Modern Web Applications:NoRails » MVC UI » RESTful API » Storage
  • 7.
    Grape » API DSL class API < Grape::API version „1' rack-based / middleware http://github.com/intridea/grape namespace :artist get “:id” do Artist.find(params[:id]).as_json end end namespace :artists do get “/” do Artist.all.as_json end end end
  • 8.
    Documentation » Developers Have the Attention Span of a Fish * * when reading documentation » Written in Markdown http://code.dblock.org/rendering-markdown-documents-in-rails » Reference will be Generated » API Sandboxes https://github.com/mmcnierney14/API-Sandbox » API Explorer https://github.com/mmcnierney14/API-Sandbox
  • 9.
    Testing an API #spec/spec_helper.rb RSpec.configure do |config| config.include RSpec::Rails::RequestExampleGroup, :type => :request, :example_group => { :file_path => /spec/api/ } end See “Writing Tests” @ https://github.com/intridea/grape
  • 10.
    Mocking is forJava Programmers describe "artworks" do before(:each) do login_as Fabricate(:admin) end describe "GET /api/v1/artwork/:slug" do it "returns an unpublished artwork" do artwork = Fabricate(:artwork, published: false) get "/api/v1/artwork/#{artwork.slug}" response.status.should == 200 response.body.at_json_path(“id”).should == artwork.slug # Pathy! end end end end
  • 11.
    Version 1 BirthsVersion 2 » Include Api_v1 » Folder-Driven Development (FDD) api/api_v1/… module Api_v1 module Api_v2 version 'v1„ version 'v2„ module Api_v1_Me module Api_v1_Me module Api_v1_Artworks module Api_v2_Artworks # ... # ... end end See “Modularizing Grape API” @ http://code.dblock.org/modularizing-a-ror-grape-api-multiple-versions
  • 12.
    Exceptions Abort Flow » Don’t question yourself, raise a hand. rescue_from :all, :backtrace => true error_format :json rescue_from Mongoid::Errors::Validations do |e| rack_response({ :message => e.message, :detail => e.document.errors, :backtrace => e.backtrace }.to_json) end end See “Grape: trapping all exceptions within the API” @ http://code.dblock.org/grape-trapping-all-exceptions-within-the-api
  • 13.
    Authentication Methods » XApp: Exchange client ID for an XApp token api/v1/api_xapp_auth.rb » OAuth 2.0: Browser-Based Redirects controllers/oauth_controller.rb » XAuth: Exchange credentials for an OAuth token controllers/oauth_controller.rb » Forms Login to Website devise/warden via user.rb See “Grape: API Authentication w/ Devise” @ http://code.dblock.org/grape-api-authentication-w-devise
  • 14.
    Authenticated Users » Unauthenticated Calls » Authorized Apps » Logged In Users, RBAC def authenticated_user authenticated error!('Unauthorized', 401) unless current_user end
  • 15.
    Object Identity » Everything has an ID » Internal ID: BSON ObjectId » External ID: humanly-readable ID » ID is the same for all API consumers » API consumers know of a single ID » When do I use a Slug? » When do I use BSON ObjectId?
  • 16.
    JSON Formats » ActiveRecord as_json passes options recursively :all – all fields visible to the object’s owner :public – all fields visible to a user with :read permissions :short – enough fields visible to a user with :read permissions, used within a collection » JSON data can be grown incrementally
  • 17.
    POST and PUT » Validate Input Parameters in Models save(hashie) valid_hash_fields :first, :last
  • 18.
    Authorization » Admins have :create, :read, :update, :delete on everything, also known as :manage » Partners have :manage on their partner data eg. partner location, get :all JSON » Users have :manage on their personal data eg. my collection, get :all JSON » Everyone has :read on public data eg. a published artwork, get :public JSON
  • 19.
    Authorization Usage » Implemented w/ CanCan cannot :read, Artwork can :read, Artwork do |artwork| artwork.published end error!(„Unauthorized', 403) unless current_user.has_authorization_to?(:delete, artist)
  • 20.
    Pagination » paginate(collection) » :offset or :page » :size Pagination Helper for Grape @ https://gist.github.com/1335242
  • 21.
    Logging » Implemented as Rack Middleware » Logs API Calls
  • 22.
    Caching » Implemented w/Rails Cache / Memcached » Key based on Class and Identity » Cache Locally » Invalidate Aggressively
  • 23.
    Cache Busting » IE9 See “IE9: Cache-Busting with Grape Middleware” @ http://code.dblock.org/ie9-cache-busting-with-grape-middleware
  • 24.
    Instrumentation » See API Stats in New Relic config/initializers/new_relic_agent_instrumentation_api.rb See “New Relic: Performance Instrumentaiton w/ Grape” @ http://code.dblock.org/new-relic-performance-instrumentation-with-grape-api
  • 25.
  • 26.
    Next » Deep Data » Caching in JSON » Generated Documentation
  • 27.
    How to designa good API and why it matters (Joshua Bloch) http://www.youtube.com/watch?v=aAb7hSCtvGw 1. Do one thing well 2. API is a Language, names matter 3. Documentation matters 4. Minimize mutability 5. Don’t make the client do anything the API could do