Client libraries help in reducing the amount of code for the application developer who is using the API, whether a REST API or any other. By adding a set of code to the application, it provides the basic things an application needs to do in order to interact with the API. This is what a client library does. Also, it may handle user authentication and authorization.
Client libraries are developed by API developer or the community.
There are several HTTP client libraries in Ruby such as:
-
HTTParty
-
Faraday
-
Built-in Net::HTTP
-
Rest-Client
-
HTTPClient
Among them, the favorite of mine is Faraday gem. Faraday has adapters for popular libraries like Net::HTTP. It is simple, flexible and supports multiple backends and embraces the concept of Rack middleware when processing the request/response cycle. Also, it’s possible to customize its behavior with middleware. We will use a connection object to start with Faraday as it’s more flexible way than a simple get
request.
conn = Faraday.new response = conn.get 'http://localhost:3000/tasks'
Using this connection object, we make HTTP requests.
params = {:title => 'Faraday gem', :created_by => 'blog'} conn.post('http://localhost:3000/tasks',params)
This will POST title = ‘Faraday gem’ and created by = ‘blog’ to http://localhost:3000/tasks. All HTTP verb methods can take an optional block that will yield aFaraday::Request
object.
conn.post do |req| req.url '/tasks' req.headers['Content-Type'] = 'application/json' req.body = '{"some": "content"}' end
Authentication
Basic and Token authentication are handled by Faraday::Request::BasicAuthentication
and Faraday::Request::TokenAuthentication
respectively. These can be added as middleware manually or through the helper methods.
conn.basic_auth('username', 'password') conn.token_auth('token')
Proxies
To specify an HTTP proxy:
Faraday.new(:proxy => 'http://proxy.example.com:80')
Using a different HTTP Adapter
Faraday provides an interface between our code and adapter. Sometimes we may want to use features that are not covered in Faraday’s interface. In such cases, we can have access to features specific to any of the adapters supported by Faraday, by passing a block when specifying the adapter to customize it. For example, you can switch to the HTTPClient adapter as below
conn = Faraday.new do |builder| builder.adapter :httpclient do |client| # yields HTTPClient client.keep_alive_timeout = 20 end end
Like this, we can switch to any of the supported adapters. The block parameters will change based on the adapters we are using.
Faraday::Connection
object middlewares
A Faraday::Connection
object has a list of middlewares, just like a Rack app. Faraday middlewares are passed as an env
hash. It has request and response information.
conn = Faraday.new conn.builder => #<Faraday::RackBuilder:0x0000000155d1f0 @handlers=[Faraday::Request::UrlEncoded, #Faraday::Adapter::NetHttp]>
Faraday::Builder
is similar to Rack::Builder
. A new Faraday::Connection
object is initialized. It has middlewares Faraday::Request::UrlEncoded
in front of an adapter Faraday::Adapter::NetHttp
. Like a Rack application, the adapter at the end of the builder chain is what actually executes the request. Middlewares are grouped into request middlewares, response middlewares, and adapters.
Faraday.new do |builder| builder.request :retry builder.request :basic_authentication, 'login', 'pass' builder.response :logger builder.adapter :net_http end
Advanced Middleware Usage
The order in which middleware is stacked in Faraday is like in Rack. The first middleware on the list wraps all others, while the last middleware is the innermost one, so that’s usually the adapter.
conn = Faraday.new(:url => 'https://redpanthers.co') do |builder| # POST/PUT params encoders: builder.request :multipart builder.request :url_encoded builder.adapter :net_http end
Middlewares stack is manipulated by the Faraday::Builder
instance. Each Faraday::Connection
instance has a Faraday::Builder
instance.
conn = Faraday.new conn.builder.swap(1, Faraday::Adapter::HTTPClient) # replace adapter conn.builder.insert(0, MyCustomMiddleware) # add middleware to beginning conn.builder.delete(MyCustomMiddleware)
Writing middleware
Middlewares are classes that respond to call. When middleware is executing, it’s passed as an env hash that has request and response information. Middleware wrap the request/response cycle. The general interface for a middleware is:
class CustomizedMiddleware def call(env) # do something with the request @app.call(env).on_complete do |env| # do something with the response end end end
All processing of the response should be done in the on-complete block. This enables middleware to work in parallel mode when many requests are occurring at the same time. After the on_complete block, env[:response] is filled in. Faraday::Response instance will be available only after `on_complete`.
faraday-middleware is a collection of various Faraday middlewares for Faraday-based API wrappers.
For testing middleware, Faraday::Adapter::Test is an HTTP adapter middleware that lets you to fake responses.
References
]]>