Faraday

Chapter 1

http_services

The Good

  • Easily understood purpose from name
  • Set the groundwork for common service consumption
  • Strictly controls the form that requests may take

The Bad

  • Inflexible
  • Strictly controls the form that requests may take
  • Significant code bloat
  • Redundant methods (read, write, create, delete)

base_client.rb

Chapter 2

Faraday (gem)

The Good

  • Very flexible
  • Easy to understand interface
  • Good community support
  • Regular release cycle
  • Highly extensible through middleware

The Bad

  • Sparsely documented
  • Relies heavily on env Hash-like object that can become very cluttered.

Chapter 3

Faraday Middleware

The Concept

Faraday implements a Rack-like structure to allow individuals to define middleware to address specific concerns for their applications. This is where the true power of Faraday lies. By only implementing what you need, you avoid having to pull in code that will never be used or is inappropriate for your use-case.

The Good

  • Maintained by the same people as Faraday
  • Strong set of general-use middleware
  • Reasonable model for new middleware development

The Bad

  • Very poor documentation
  • Pulling in the FM project is counter to the idea that you only pull in what is absolutely needed.
  • A bit of a learning curve, partially due to poor documentation.
  • Did I mention the documentation?

FaradayMiddleware (gem)

The Good

This is just one more example of pre-existing middleware that is available. It is also very well documented and tested. Even if we didn't want to use this project directly, it would likely be a strong model for developing our own middleware.

The Bad

One more external dependency.

FaradayConductivity

Take Note:

While it is relatively easy to write custom middleware, we found certain cases where the middleware does something to the request and depends on the response to do something and responds to it. This came up when working on OAuth caching.

 

def call(env)
  attempts = @retries + 1
  request_body = env[:body]
  reuse = true

  begin
    env[:body] = request_body
    env[:request_headers][AUTH_HEADER] = auth_header(reuse)
    response = @app.call(env)

    reuse = false if expired_header?(response)
    attempts -= 1
  end until attempts == 0 || !expired_header?(response)

  response
end

Chapter 4

Potential Implementations

http_services MkII

In this approach, we would keep http_services around and update it to consume Faraday, rather than a specific adapter. Further, we could use it to house common middleware that will be used by all applications and clients. There is also the added benefit of having a common connection housed here. This would still allow individual projects to create custom middleware for their own unique use-cases.

Connection Per Client

This approach is similar to the first but focuses on each client building the Faraday connection on its own. This is good because it gives each client fine control over what the connection and call stack looks like. However, it results in a lot of repeated code between clients, due to largely similar functionality.

def initialize(options)
  @connection = Faraday.new options[:base_uri] do |faraday|
    faraday.headers = HttpServices::HEADERS.merge(HttpServices.user_agent_header(self.to_s))
    faraday.options.timeout = options[:read_timeout] || 6
    faraday.options.open_timeout = options[:open_timeout]

    faraday.use :abacus_timing, client_name: self.to_s
    faraday.request :correlation_header
    faraday.request :oauth_cache, options
    faraday.request :retry, max: (options[:retries] || 2), interval: 0.05
    faraday.response :faraday_logging, client_name: self.to_s

    faraday.adapter Faraday.default_adapter
  end
end

Links

Faraday

By Ryan Perrin

Faraday

Faraday presentation for the February Ruby Dev Meet-Up

  • 732