dideler
11/11/2014 - 1:22 AM

API Best Practicies

API Best Practicies

#API Best Practices
##Routes
Restricting Routes											
    resources :zombies, only: [:index, :show]
    resources :humans, except: [:destroy, :edit, :update] 

##Subdomain						
Keeping our API under its own subdomain allows load balancing traffic at the DNS level. 
    resources :zombies, constraints: { subdomain: 'api' }
    resources :humans, constraints: { subdomain: 'api' }
		
or
						
    constraints subdomain: 'api' do									
        resources :zombies
        resources :humans						
    end 

http://api.cs-zombies.com/zombies						
http://api.cs-zombies.com/humans					
						
/etc/hosts						
    127.0.0.1 cs-zombies-dev.com
    127.0.0.1 api.cs-zombies-dev.com

makes these urls available on local machine						
    http://cs-zombies-dev.com :3000 
    http://api.cs-zombies-dev.com :3000 

						
Prefix Verb
zombies GET
             POST					
 new_zombie GET
						
###URI Pattern
/zombies(.:format)
/zombies(.:format)
/zombies/new(.:format)
						
Controller#Action
    zombies#index {:subdomain=>"api"}
    zombies#create {:subdomain=>"api"}
    zombies#new {:subdomain=>"api"}

##Namespace
config/routes					
    namespace :api do					
        resources :zombies
    end


app/controllers/api/zombies_controller.rb					
    module Api
       class ZombiesController < ApplicationController
       end						
    end 

config/routes						
    constraints subdomain: 'api' do
        namespace :api do					
        resources :zombies
       end						
    end
    
creates:
http://api.cs-zombies.com/api/zombies

or removes the duplication in the namespace
    constraints subdomain: 'api' do
        namespace :api, path: '/' do
        end
    end

and creates:
http://api.cs-zombies.com/zombies


##GET Requests
Important characteristics:
• Safe - it should not take any action other than retrieval.
• Idempotent - sequential GET requests to the same URI should not generate side-effects.

module API
    class ZombiesController < ApplicationController
        def index
            zombies = Zombie.all
            render json: zombies
        end
    end
end

The to_json method serializes all properties to JSON
    zombies.to_json = {"id":5,"name":"Joanna","age":null,"created_at":"2014-01-17T18:40:40.195Z","updated_at":"2014-01-17T18:40:40.195Z","weapon":"axe"}
    
###Curl
curl http://api.cs-zombies-dev.com:3000/zombies

Flags
-I option to only display response headers
-H option to send custom request headers
-X option specifies the method

###Media Types
Media types specify the scheme for resource representations.

class ZombiesController < ApplicationController
def index
    zombies = Zombie.all
        respond_to do |format|
            format.json { render json: zombies, status: 200 }
            format.xml { render xml: zombies, status: 200 }
        end
    end
end

Rails ships with 21 different media types out of the box.
Mime::SET.collect(&:to_s)



##POST Requests
A couple of things are expected from a successful POST request:
• The status code for the response should be 201 - Created.
• The response body should contain a representation of the new resource.
• The Location header should be set with the location of the new resource.

201 - Created means the request has been fulfilled and resulted in a new resource being created

    def create
        episode = Episode.new(episode_params)
        if episode.save
            render json: episode, status: 201, location: episode
        end
    end

204 - No Content means the server has fulfilled the request but does not need to return an entity-body
422 - Unprocessable Entity means the client submitted request was well-formed but semantically invalid.

Rails checks for an authenticity token on POST, PUT/PATCH and DELETE.
	class ApplicationController < ActionController::Base
		# Prevent CSRF attacks by raising an exception.
		# For APIs, you may want to use :null_session instead.
		protect_from_forgery with:
	end

config/environments/test.rb
    # Disable request forgery protection in test environment.
    config.action_controller.allow_forgery_protection = false
    
Some successful responses might not need to include a response body. Ajax responses can be made a lot faster with no response body.
    def create
        episode = Episode.new(episode_params)
        if episode.save
            render nothing: true, status: 204, location: episode
        end
    end
    
The head method creates a response consisting solely of HTTP headers.
    def create
        episode = Episode.new(episode_params)
        if episode.save
            head 204, location: episode
        end
    end
    
(or 'head :no_content')

Unsuccessful Requests
    def create
        episode = Episode.new(episode_params)
        if episode.save
            render json: episode, status: :created, location: episode
        else
            render json: episode.errors, status: 422
        end
    end
    
500 - Internal Server Error means the server encountered an unexpected condition which prevented it from fulfilling the request


##Versioning

###Versioning Using the URI
config/routes.rb
    namespace :v1 do
        resources :zombies
    end
    
    namespace :v2 do
        resources :zombies
    end

app/controllers/v1/zombies_controller.rb    
    module V1
        class ZombiesController < ApplicationController
            before_action ->{ @remote_ip = request.headers['REMOTE_ADDR'] }
    
            def index
                render json: "#{@remote_ip} Version One!", status: 200
            end
        end
    end

    
If an app strictly serves a web API, it’s ok to use ApplicationController as the base class.
app/controllers/application_controller.rb
    class ApplicationController < ActionController::Base
        before_action ->{ @remote_ip = request.headers['REMOTE_ADDR'] }
    end


Error Handling
Development Tools
Postman

[Understanding REST Headers and Parameters](http://www.soapui.org/Best-Practices/understanding-rest-headers-and-parameters.html)

###Documentation

###Testing

Tools: Use Rspec Requests w/ Webmock

Rails API Integration Testing

Rails API Testing Best Practices