Consuming API's the right way.

@vjustov

A little backstory

 

Enter HER

gem 'her'
# config/initializers/her.rb
Her::API.setup url: "https://api.example.com" do |c|
  # Request
  c.use Faraday::Request::UrlEncoded

  # Response
  c.use Her::Middleware::DefaultParseJSON

  # Adapter
  c.use Faraday::Adapter::NetHttp
end
class User
  include Her::Model
end
User.all
# GET "https://api.example.com/users" and return a collection of Users

User.find(1)
# GET "https://api.example.com/users/1" and return a User object

@user = User.create(fullname: "Tobias Fünke")
# POST "https://api.example.com/users" with `fullname=Tobias+Fünke` 
# and return the saved User object

@user = User.new(fullname: "Tobias Fünke")
@user.occupation = "actor"
@user.save
# POST "https://api.example.com/users" with `fullname=Tobias+Fünke&occupation=actor` 
# and return the saved User object

@user = User.find(1)
@user.fullname = "Lindsay Fünke"
@user.save
# PUT "https://api.example.com/users/1" with `fullname=Lindsay+Fünke` 
# and return the updated User object
Her::API.setup url: "https://api.example.com" do |c|
  connection.use TokenAuthentication
  connection.use Faraday::Request::UrlEncoded
  connection.use FaradayMiddleware::Caching, memcache
  connection.use APIParser
  connection.use FaradayMiddleware::Instrumentation
  connection.use Faraday::Adapter::NetHttp
end
  

API


CLIENT

Business Logic

Caching

Content Negotiation

Authentication

Parsing

HTTP Errors Handling

Statelessness

Business Error Handling

Respect the Status code

http://httpstatusrappers.com/200.html

HTTP Errors Handling

Timeouts

Caching API responses locally

Content Negotiation

Handling Rate-Limits

Response Mapping

ResourceKit

class BookResource < ResourceKit::Resource
  resources do
    default_handler(410) { |response| fail BookExpiredException }
    default_handler(404) { |response| fail RecordNotFoundException }

    action :find do
      path '/api/v1/books/:id'

      handler(200) do |response|
        BookMapping.extract_single(response.body, :read)
      end
    end
  end
end

BookResource.new(connection: GoodReadsAPIClient.instance.connection)

Kartographer

class BookMapping
  include Kartograph::DSL

  kartograph do
    mapping Book

    property :id, scopes: [:read, :list]
    property :name, scopes: [:read, :list, :create]
    property :pages_count, scopes: [:read]
    property :slug, scopes: [:read]
    property :isbn, scopes: [:read, :list]
    property :print_date, scopes: [:read]
    property :end_date, scopes: [:read]
    property :edition, scopes: [:read]
  end
end


book = Book.new(name: 'The tales of Bobby Tables')
json_for_create = BookMapping.representation_for(:create, book)

Virtus


  class Book
    include Virtus.model

    attribute :id
    attribute :name
    attribute :pages_count
    attribute :slug
    attribute :isbn
    attribute :print_date

    def in_stock?
      Inventory.check?(isbn)
    end
  end

Bop it

Twist it

Pull it

Wrap it

Made with Slides.com