Building RESTful APIs w/ Grape
-
Upload
daniel-doubrovkine -
Category
Technology
-
view
18.684 -
download
0
description
Transcript of Building RESTful APIs w/ Grape
RESTful APIs w/ GrapeTuesday, November 8th 2011
Daniel Doubrovkine / [email protected] @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
[email protected] do |artist| { 'first_name': '<%= @artist.first_name.to_json %>', 'last_name': '<%= @artist.last_name.to_json %>' }
» 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 …
Occupy Rails?
Modern Web Applications: NoRails
» MVC UI
» RESTful API
» Storage
» API DSLrack-based / middlewarehttp://github.com/intridea/grape
Grape
class API < Grape::API version ‘1'
namespace :artist get “:id” do Artist.find(params[:id]).as_json end end
namespace :artists do get “/” do Artist.all.as_json end endend
Documentation
» Developers Have the Attention Span of a Fish ** when reading documentation
» Written in Markdownhttp://code.dblock.org/rendering-markdown-documents-in-rails
» Reference will be Generated
» API Sandboxeshttps://github.com/mmcnierney14/API-Sandbox
» API Explorerhttps://github.com/mmcnierney14/API-Sandbox
# spec/spec_helper.rb
RSpec.configure do |config|
config.include RSpec::Rails::RequestExampleGroup, :type => :request, :example_group => {
:file_path => /spec\/api/ }
end
Testing an API
See “Writing Tests” @ https://github.com/intridea/grape
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
Mocking is for Java Programmers
Version 1 Births Version 2
» Include Api_v1» Folder-Driven Development (FDD)
api/api_v1/…
module Api_v1 version 'v1‘ module Api_v1_Me
module Api_v1_Artworks # ...end
module Api_v2 version 'v2‘ module Api_v1_Me
module Api_v2_Artworks # ...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 tokenapi/v1/api_xapp_auth.rb
» OAuth 2.0: Browser-Based Redirectscontrollers/oauth_controller.rb
» XAuth: Exchange credentials for an OAuth tokencontrollers/oauth_controller.rb
» Forms Login to Websitedevise/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 dataeg. partner location, get :all JSON
» Users have :manage on their personal dataeg. my collection, get :all JSON
» Everyone has :read on public dataeg. a published artwork, get :public JSON
Authorization Usage
cannot :read, Artworkcan :read, Artwork do |artwork| artwork.publishedend
error!(‘Unauthorized', 403) unless current_user.has_authorization_to?(:delete, artist)
» Implemented w/ CanCan
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 Relicconfig/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
Thank you.
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