How to Write a Faraday Request Middleware

Photo by Tekton on Unsplash

Faraday is a very powerful tool, and you can make it even more powerful.

TL;DR

Welcome

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

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

The MyLogger Class

lib/my_logger.rb

# frozen_string_literal: trueclass MyLogger
def info(text_to_log)
puts text_to_log
end
end

The Faraday Middleware

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: truemodule 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

lib/faraday_middleware/log_requests.rb

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

Using 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

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

Originally published at https://brandoncc.dev.