-
-
Save rosiehoyem/2a38083497e2edde7dce to your computer and use it in GitHub Desktop.
API Best Practicies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment