File: README.md
[][website]
Faraday is an HTTP client library abstraction layer that provides a common interface over many
adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.
Take a look at Awesome Faraday for a list of available adapters and middleware.
Why use Faraday?
Faraday gives you the power of Rack middleware for manipulating HTTP requests and responses,
making it easier to build sophisticated API clients or web service libraries that abstract away
the details of how HTTP requests are made.
Faraday comes with a lot of features out of the box, such as:
- Support for multiple adapters (Net::HTTP, Typhoeus, Patron, Excon, HTTPClient, and more)
- Persistent connections (keep-alive)
- Parallel requests
- Automatic response parsing (JSON, XML, YAML)
- Customization of the request/response cycle with middleware
- Support for streaming responses
- Support for uploading files
- And much more!
Getting Started
The best starting point is the Faraday Website, with its introduction and explanation.
Need more details? See the Faraday API Documentation to see how it works internally, or take a look at Advanced techniques for calling HTTP APIs in Ruby blog post from @mattbrictson ๐
Supported Ruby versions
This library aims to support and is tested against the currently officially supported Ruby
implementations. This means that, even without a major release, we could add or drop support for Ruby versions,
following their EOL.
Currently that means we support Ruby 3.0+
If something doesn't work on one of these Ruby versions, it's a bug.
This library may inadvertently work (or seem to work) on other Ruby
implementations and versions, however support will only be provided for the versions listed
above.
If you would like this library to support another Ruby version, you may
volunteer to be a maintainer. Being a maintainer entails making sure all tests
run and pass on that implementation. When something breaks on your
implementation, you will be responsible for providing patches in a timely
fashion. If critical issues for a particular implementation exist at the time
of a major release, support for that Ruby version may be dropped.
Contribute
Do you want to contribute to Faraday?
Open the issues page and check for the help wanted label!
But before you start coding, please read our Contributing Guide
Copyright
© 2009 - 2026, the Faraday Team. Website and branding design by Elena Lo Piccolo.
File: docs/adapters/custom/index.md
Writing custom adapters
!> A template for writing your own custom adapter 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
endNow, there are only two things which are actually mandatory for an adapter middleware to function:
- a
#callimplementation - a call to
#save_responseinside#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
endImplementing `#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
endHelper 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
envobject and save the response into it. - Set the
:responsekey in theenvobject. - Parse headers using
Utils::Headersand set the:response_headerskey in theenvobject. - Call
#finishon theResponseobject, triggering the#on_completecallbacks 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
endRegister 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)File: docs/adapters/custom/parallel-requests.md
Adding support for parallel requests
!> This is slightly more involved, and this section is not fully formed.
Vague example, excerpted from the test suite about parallel requests
response_1 = nil
response_2 = nil
conn.in_parallel do
response_1 = conn.get('/about')
response_2 = conn.get('/products')
end
puts response_1.status
puts response_2.statusFirst, in your class definition, you can tell Faraday that your backend supports parallel operation:
class FlorpHttp < ::Faraday::Adapter
dependency do
require 'florp_http'
end
self.supports_parallel = true
endThen, implement a method which returns a ParallelManager:
class FlorpHttp < ::Faraday::Adapter
dependency do
require 'florp_http'
end
self.supports_parallel = true
def self.setup_parallel_manager(_options = nil)
FlorpParallelManager.new # NB: we will need to define this
end
def call(env)
# NB: you can call `in_parallel?` here to check if the current request
# is part of a parallel batch. Useful if you need to collect all requests
# into the ParallelManager before running them.
end
end
class FlorpParallelManager
# The execute method will be passed the same block as `in_parallel`,
# so you can either collect the requests or just wrap them into a wrapper,
# depending on how your adapter works.
def execute(&block)
run_async(&block)
end
endA note on the old, deprecated interface
Prior to the introduction of the execute method, the ParallelManager was expected to implement a run method
and the execution of the block was done by the Faraday connection BEFORE calling that method.
This approach made the ParallelManager implementation harder and forced you to keep state around.
The new execute implementation allows to avoid this shortfall and support different flows.
As of Faraday 2.0, run is still supported in case execute is not implemented by the ParallelManager,
but this method should be considered deprecated.
For reference, please see an example using run from em-synchrony
and its ParallelManager implementation.
File: docs/adapters/custom/streaming.md
Adding support for streaming
Faraday supports streaming responses, which means that the response body is not loaded into memory all at once,
but instead it is read in chunks. This can be particularly useful when dealing with large responses.
Not all HTTP clients support this, so it is not required for adapters to support it.
However, if you do want to support it in your adapter, you can do so by leveraging helpers provided by the env object.
Let's see an example implementation first with some comments, and then we'll explain it in more detail:
module Faraday
class Adapter
class FlorpHttp < Faraday::Adapter
def call(env)
super
if env.stream_response? # check if the user wants to stream the response
# start a streaming response.
# on_data is a block that will let users consume the response body
http_response = env.stream_response do |&on_data|
# perform the request using FlorpHttp
# the block will be called for each chunk of data
FlorpHttp.perform_request(...) do |chunk|
on_data.call(chunk)
end
end
# the body is already consumed by the block
# so it's good practice to set it to nil
http_response.body = nil
else
# perform the request normally, no streaming.
http_response = FlorpHttp.perform_request(...)
end
save_response(env, http_response.status, http_response.body, http_response.headers, http_response.reason_phrase)
end
end
end
endHow it works
`#stream_response?`
The first helper method we use is #stream_response?. This method is provided by the env object and it returns true
if the user wants to stream the response. This is controlled by the presence of an on_data callback in the request options.
`#stream_response`
The second helper is #stream_response. This method is also provided by the env object and it takes a block.
The block will be called with a single argument, which is a callback that the user can use to consume the response body.
All your adapter needs to do, is to call this callback with each chunk of data that you receive from the server.
The on_data callback will internally call the callback provided by the user, so you don't need to worry about that.
It will also keep track of the number of bytes that have been read, and pass that information to the user's callback.
To see how this all works together, let's see an example of how a user would use this feature:
# A buffer to store the streamed data
streamed = []
conn = Faraday.new('http://httpbingo.org')
conn.get('/stream/100') do |req|
# Set a callback which will receive tuples of chunk Strings,
# the sum of characters received so far, and the response environment.
# The latter will allow access to the response status, headers and reason, as well as the request info.
req.options.on_data = proc do |chunk, overall_received_bytes, env|
puts "Received #{overall_received_bytes} characters"
streamed << chunk
end
end
# Joins all response chunks together
streamed.joinFor more details on the user experience, check the Streaming Responses page.
File: docs/adapters/custom/testing.md
Test your custom adapter
Faraday puts a lot of expectations on adapters, but it also provides you with useful tools to test your adapter
against those expectations. This guide will walk you through the process of testing your adapter.
The adapter test suite
Faraday provides a test suite that you can use to test your adapter.
The test suite is located in the spec/external_adapters/faraday_specs_setup.rb.
All you need to do is to require 'faraday_specs_setup' in your adapter's spec_helper.rb file.
This will load the an adapter shared example group that you can use to test your adapter.
require 'faraday_specs_setup'
RSpec.describe Faraday::Adapter::FlorpHttp do
it_behaves_like 'an adapter'
# You can then add any other test specific to this adapter here...
endTesting optional features
By default, an adapter will test your adapter against the required behaviour for an adapter.
However, there are some optional "features" that your adapter can implement, like parallel requests or streaming.
If your adapter implements any of those optional features, you can test it against those extra expectations
by calling the features method:
RSpec.describe Faraday::Adapter::MyAdapter do
# Since not all adapters support all the features Faraday has to offer, you can use
# the `features` method to turn on only the ones you know you can support.
features :request_body_on_query_methods,
:compression,
:streaming
# Runs the tests provide by Faraday, according to the features specified above.
it_behaves_like 'an adapter'
# You can then add any other test specific to this adapter here...
endAvailable features
| Feature | Description |
|---|---|
:compression |
Tests that your adapter can handle gzip and deflate compressions. |
:local_socket_binding |
Tests that your adapter supports binding to a local socket via the :bind request option. |
:parallel |
Tests that your adapter supports parallel requests. See Parallel requests for more details. |
:reason_phrase_parse |
Tests that your adapter supports parsing the reason_phrase from the response. |
:request_body_on_query_methods |
Tests that your adapter supports sending a request body on GET, HEAD, DELETE and TRACE requests. |
:streaming |
Tests that your adapter supports streaming responses. See Streaming for more details. |
:trace_method |
Tests your adapter against the TRACE HTTP method. |
File: docs/adapters/index.md
Adapters
The Faraday Adapter interface determines how a Faraday request is turned into
a Faraday response object. Adapters are typically implemented with common Ruby
HTTP clients, but can have custom implementations. Adapters can be configured
either globally or per Faraday Connection through the configuration block.
For example, consider using httpclient as an adapter. Note that faraday-httpclient must be installed beforehand.
If you want to configure it globally, do the following:
require 'faraday'
require 'faraday/httpclient'
Faraday.default_adapter = :httpclientIf you want to configure it per Faraday Connection, do the following:
require 'faraday'
require 'faraday/httpclient'
conn = Faraday.new do |f|
f.adapter :httpclient
endFantastic adapters and where to find them
Except for the default Net::HTTP adapter and the Test Adapter adapter, which is for test purposes only,
adapters are distributed separately from Faraday and need to be manually installed.
They are usually available as gems, or bundled with HTTP clients.
While most adapters use a common Ruby HTTP client library, adapters can also
have completely custom implementations.
If you're just getting started you can find a list of featured adapters in Awesome Faraday.
Anyone can create a Faraday adapter and distribute it. If you're interested learning more, check how to build your own!
File: docs/adapters/net-http.md
Net::HTTP Adapter
Faraday's Net::HTTP adapter is the default adapter. It uses the Net::HTTP
library that ships with Ruby's standard library.
Unless you have a specific reason to use a different adapter, this is probably
the adapter you want to use.
With the release of Faraday 2.0, the Net::HTTP adapter has been moved into a separate gem,
but it has also been added as a dependency of Faraday.
That means you can use it without having to install it or require it explicitly.
File: docs/adapters/test-adapter.md
Test Adapter
The built-in Faraday Test adapter lets you define stubbed HTTP requests. This can
be used to mock out network services in an application's unit tests.
The easiest way to do this is to create the stubbed requests when initializing
a Faraday::Connection. Stubbing a request by path yields a block with aFaraday::Env object. The stub block expects an Array return value with three
values: an Integer HTTP status code, a Hash of key/value headers, and a
response body.
conn = Faraday.new do |builder|
builder.adapter :test do |stub|
# block returns an array with 3 items:
# - Integer response status
# - Hash HTTP headers
# - String response body
stub.get('/ebi') do |env|
[
200,
{ 'Content-Type': 'text/plain', },
'shrimp'
]
end
# test exceptions too
stub.get('/boom') do
raise Faraday::ConnectionFailed
end
end
endYou can define the stubbed requests outside of the test adapter block:
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/tamago') { |env| [200, {}, 'egg'] }
endThis Stubs instance can be passed to a new Connection:
conn = Faraday.new do |builder|
builder.adapter :test, stubs do |stub|
stub.get('/ebi') { |env| [ 200, {}, 'shrimp' ]}
end
endIt's also possible to stub additional requests after the connection has been
initialized. This is useful for testing.
stubs.get('/uni') { |env| [ 200, {}, 'urchin' ]}You can also stub the request body with a string or a proc.
It would be useful to pass a proc if it's OK only to check the parts of the request body are passed.
stubs.post('/kohada', 'where=sea&temperature=24') { |env| [ 200, {}, 'spotted gizzard shad' ]}
stubs.post('/anago', -> (request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'Wakamoto' } }) { |env| [200, {}, 'conger eel'] }If you want to stub requests that exactly match a path, parameters, and headers,strict_mode would be useful.
stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true) do |stub|
stub.get('/ikura?nori=true', 'X-Soy-Sauce' => '5ml' ) { |env| [200, {}, 'ikura gunkan maki'] }
endThis stub expects the connection will be called like this:
conn.get('/ikura', { nori: 'true' }, { 'X-Soy-Sauce' => '5ml' } )If there are other parameters or headers included, the Faraday Test adapter
will raise Faraday::Test::Stubs::NotFound. It also raises the error
if the specified parameters (nori) or headers (X-Soy-Sauce) are omitted.
You can also enable strict_mode after initializing the connection.
In this case, all requests, including ones that have been already stubbed,
will be handled in a strict way.
stubs.strict_mode = trueFinally, you can treat your stubs as mocks by verifying that all of the stubbed
calls were made. NOTE: this feature is still fairly experimental. It will not
verify the order or count of any stub.
stubs.verify_stubbed_callsAfter the test case is completed (possibly in an after hook), you should reset
the stubs so each test starts with its own set. When you hold a reference to theStubs instance, call clear to remove every stub, including consumed ones.
stubs.clearAlternatively, if you rely on the default connection, you can clear it to prevent
it from being cached between different tests.
Faraday.default_connection = nilExamples
Working RSpec and test/unit examples for a fictional JSON API client are
available.
File: docs/advanced/parallel-requests.md
Parallel Requests
Some adapters support running requests in parallel.
This can be achieved using the #in_parallel method on the connection object.
# Install the Typhoeus adapter with `gem install faraday-typhoeus` first.
require 'faraday/typhoeus'
conn = Faraday.new('http://httpbingo.org') do |faraday|
faraday.adapter :typhoeus
end
now = Time.now
conn.in_parallel do
conn.get('/delay/3')
conn.get('/delay/3')
end
# This should take about 3 seconds, not 6.
puts "Time taken: #{Time.now - now}"A note on Async
You might have heard about Async and its native integration with Ruby 3.0.
The good news is that you can already use Async with Faraday (thanks to the async-http-faraday gem)
and this does not require the use of #in_parallel to run parallel requests.
Instead, you only need to wrap your Faraday code into an Async block:
# Install the Async adapter with `gem install async-http-faraday` first.
require 'async/http/faraday'
conn = Faraday.new('http://httpbingo.org') do |faraday|
faraday.adapter :async_http
end
now = Time.now
# NOTE: This is not limited to a single connection anymore!
# You can run parallel requests spanning multiple connections.
Async do
Async { conn.get('/delay/3') }
Async { conn.get('/delay/3') }
end
# This should take about 3 seconds, not 6.
puts "Time taken: #{Time.now - now}"The big advantage of using Async is that you can now run parallel requests spanning multiple connections,
whereas the #in_parallel method only works for requests that are made through the same connection.
File: docs/advanced/streaming-responses.md
Streaming Responses
Sometimes you might need to receive a streaming response.
You can do this with the on_data request option.
The on_data callback is a receives tuples of chunk Strings, and the total
of received bytes so far.
This example implements such a callback:
# A buffer to store the streamed data
streamed = []
conn = Faraday.new('http://httpbingo.org')
conn.get('/stream/100') do |req|
# Set a callback which will receive tuples of chunk Strings,
# the sum of characters received so far, and the response environment.
# The latter will allow access to the response status, headers and reason, as well as the request info.
req.options.on_data = Proc.new do |chunk, overall_received_bytes, env|
puts "Received #{overall_received_bytes} characters"
streamed << chunk
end
end
# Joins all response chunks together
streamed.joinThe on_data streaming is currently only supported by some adapters.
To see which ones, please refer to Awesome Faraday comparative table or check the adapter documentation.
Moreover, the env parameter was only recently added, which means some adapters may only have partial support
(i.e. only chunk and overall_received_bytes will be passed to your block).