Writing custom adapters¶ ↑
!> A template for writing your own middleware is available in the faraday-adapter-template repository.
Adapters have methods that can help you implement support for a new backend.
This example will use a fictional HTTP backend gem called FlorpHttp
. It doesn’t exist. Its only function is to make this example more concrete.
An Adapter is a Middleware¶ ↑
When you subclass Faraday::Adapter
, you get helpful methods defined and all you need to do is to extend the call
method (remember to call super
inside it!):
module Faraday class Adapter class FlorpHttp < Faraday::Adapter def call(env) super # Perform the request and call `save_response` end end end end
Now, there are only two things which are actually mandatory for an adapter middleware to function:
-
a call implementation
-
a call to save_response inside call, which will keep the Response around.
These are the only two things, the rest of this text is about methods which make the authoring easier.
Like any other middleware, the env
parameter passed to call is an instance of Faraday::Env. This object will contain all the information about the request, as well as the configuration of the connection. Your adapter is expected to deal with SSL and Proxy settings, as well as any other configuration options.
Connection options and configuration block¶ ↑
Users of your adapter have two main ways of configuring it: * connection options: these can be passed to your adapter initializer and are automatically stored into an instance variable @connection_options
. * configuration block: this can also be provided to your adapter initializer and it’s stored into an instance variable @config_block
.
Both of these are automatically managed by Faraday::Adapter#initialize
, so remember to call it with super
if you create an initialize
method in your adapter. You can then use them in your adapter code as you wish, since they’re pretty flexible.
Below is an example of how they can be used:
# You can use @connection_options and @config_block in your adapter code class FlorpHttp < Faraday::Adapter def call(env) # `connection` internally calls `build_connection` and yields the result connection do |conn| # perform the request using configured `conn` end end def build_connection(env) conn = FlorpHttp::Client.new(pool_size: @connection_options[:pool_size] || 10) @config_block&.call(conn) conn end end # Then your users can provide them when initializing the connection Faraday.new(...) do |f| # ... # in this example, { pool_size: 5 } will be provided as `connection_options` f.adapter :florp_http, pool_size: 5 do |client| # this block is useful to set properties unique to HTTP clients that are not # manageable through the Faraday API client.some_fancy_florp_http_property = 10 end end
Implementing close¶ ↑
Just like middleware, adapters can implement a close method. It will be called when the connection is closed. In this method, you should close any resources that you opened in initialize or call, like sockets or files.
class FlorpHttp < Faraday::Adapter def close @socket.close if @socket end end
Helper methods¶ ↑
Faraday::Adapter
provides some helper methods to make it easier to implement adapters.
save_response¶ ↑
The most important helper method and the only one you’re expected to call from your call method. This method is responsible for, among other things, the following: * Take the env
object and save the response into it. * Set the :response
key in the env
object. * Parse headers using Utils::Headers
and set the :response_headers
key in the env
object. * Call finish on the Response
object, triggering the on_complete callbacks in the middleware stack.
class FlorpHttp < Faraday::Adapter def call(env) super # Perform the request using FlorpHttp. # Returns a FlorpHttp::Response object. response = FlorpHttp.perform_request(...) save_response(env, response.status, response.body, response.headers, response.reason_phrase) end end
save_response also accepts a finished
keyword argument, which defaults to true
, but that you can set to false if you don’t want it to call finish on the Response
object.
request_timeout¶ ↑
Most HTTP libraries support different types of timeouts, like :open_timeout
, :read_timeout
and :write_timeout
. Faraday
let you set individual values for each of these, as well as a more generic :timeout
value on the request options.
This helper method knows about supported timeout types, and falls back to :timeout
if they are not set. You can use those when building the options you need for your backend’s instantiation.
class FlorpHttp < Faraday::Adapter def call(env) super # Perform the request using FlorpHttp. # Returns a FlorpHttp::Response object. response = FlorpHttp.perform_request( # ... other options ..., open_timeout: request_timeout(:open, env[:request]), read_timeout: request_timeout(:read, env[:request]), write_timeout: request_timeout(:write, env[:request]) ) # Call save_response end end
Register your adapter¶ ↑
Like middleware, you may register a nickname for your adapter. People can then refer to your adapter with that name when initializing their connection. You do that using Faraday::Adapter.register_middleware
, like this:
class FlorpHttp < Faraday::Adapter # ... end Faraday::Adapter.register_middleware(florp_http: FlorpHttp)