BlogNotesAbout
moon indicating dark mode
sun indicating light mode

How to Write a Faraday Request Middleware

May 31, 2020

TL;DR

Check out the repo to see the files we create in this blog post.

Welcome

Faraday is a Ruby web request library. Ruby’s standard library includes tools such as Net::HTTP to make requests, but using them can be a little unwieldy, especially when it comes to handling errors. Faraday abstracts all of that away, giving you a nice interface.

I recently needed to add the ability to cache the last request I made, so that I could send it to Rollbar if it failed. I had to dig through the source code, as well as implementations such as this one and this one in order to figure out how to write middleware.

My goal with this blog post is to help you understand how to write a request middleware of your own. Response middleware should use most of the same code, but they require use of the #on_complete method access to the response in a safe way.

I highly recommend taking a look at the Faraday Middleware Docs to get an idea what middleware can do for you, and what it is.

Setup

The first thing you will need is your actual middleware file. This consists of a class which can be instantiated during the request. The class will receive the Faraday state (colloquially known as app) as its first instantiation argument, and any arguments you give when utilizing your middleware as subsequent arguments.

There are only two things you need to do in order to satisfy Faraday:

  1. Call #super with app in #initialize.
  2. Add a #call method, which is where the middleware does its processing. You need to call app.call with the environment, which is provided to the #call method as an argument.

I realize #2 is probably a little confusing, but you can see it in action in lib/faraday_middleware/request/log_requests.rb below.

Building Our Middleware

We will be building a basic request logger together.

The MyLogger Class

The first thing we need is a logger. You could use any logger for this, but for simplicity, we will create a very basic one:

lib/my_logger.rb

# frozen_string_literal: true
class MyLogger
def info(text_to_log)
puts text_to_log
end
end

The Faraday Middleware

What would a middleware be without a middleware class? We’ll create that next:

The middleware will accept the Faraday state as the first argument, then a logger as a second argument. We will store both of those for use in the #call method.

lib/faraday_middleware/request/log_requests.rb

# frozen_string_literal: true
module FaradayMiddleware
class LogRequests < Faraday::Middleware
def initialize(app, logger)
super(app)
@app = app
@logger = logger
end
def call(env)
# any processing you want, including altering the environment
# (see https://lostisland.github.io/faraday/middleware/custom)
@logger.info("Using method #{env.method} to access #{env.url}")
# You must do this when you are done with your processing
@app.call(env)
end
end
end

The Middleware loader

Before we can use a Faraday middleware, we need to register it with Faraday. We’re going to use the Kernel#autoload method so that our middleware is not loaded until it is actually used. This is the convention given by Faraday themselves, in the middleware that they offer and maintain.

lib/faraday_middleware/log_requests.rb

# frozen_string_literal: true
require 'faraday'
module FaradayMiddleware
# load the middleware from the relative directory ./request
autoload :LogRequests, "#{File.dirname(__FILE__)}/request/log_requests.rb"
# register the middleware under the key :log_requests, for later use
Faraday::Request.register_middleware log_requests: -> { LogRequests }
end

Using Our Middleware

The only thing left to do is actually use our middleware!

Let’s create a basic script that will make a web request

example_usage.rb

# frozen_string_literal: true
# we need faraday
require 'faraday'
# we need our logger
require_relative 'lib/my_logger'
# we need our middleware
require_relative 'lib/faraday_middleware/log_requests'
# create an instance of our logger to pass into our middleware
logger = MyLogger.new
# create a new Faraday connection interface
connection = Faraday.new(url: 'https://brandoncc.dev') do |conn|
# use our middleware
conn.request :log_requests, logger
end
connection.get('/')

and use it:

$ ruby example_usage.rb
Using method get to access https://brandoncc.dev/

That’s it! In a few minutes, we were able to tap into the request cycle and add some logic that we needed. I hope I have successfully shown you how valuable middleware can be, and how to use it with Faraday to improve your scripts and applications.

I highly recommend checking out the middleware offered by the Faraday team, because you might find what you are looking for already exists.

Thank You, Friend

Thanks for spending some of your valuable time reading my blog post, and I hope to see you again!


Brandon Conway
I enjoy learning about and writing code in many programming languages