Because the Rack interface is so simple, you can use any code that implements this interface in a Rack application.

This allows you to build small, focused, and reusable applications which work together to provide different functionalities. These mini-components are known as Middleware.

Middleware sits between the user and the application code. When an HTTP request comes in, the middleware can intercept, examine, and modify it. Similarly, it can examine and modify the HTTP response before forwarding it to the user.

Middleware is very useful for writing logic that is not specific to your web application, such as authenticating the request, logging, or exception handling. It focuses on doing one thing and doing it well.

middleware.png

For example, here’s a middleware that logs to the console

  1. before it passes the request to the next application in the pipeline and

  2. after it receives the response on its way out.

class Logger
  def initialize(app)
    @app = app
  end

  def call(env)
    puts "Received the incoming request"

    # forward the request to the next middleware or app 
    status, headers, body = @app.call(env)

    puts "Received the outgoing response"

    [status, headers, body]
  end
end
  1. This middleware accepts the app in the constructor. The app can be another middleware in the pipeline or the actual application.

  2. When called, it first deals with the incoming request represented by the env object. Here, we print “Received the incoming request”.

  3. After handling the request, it passes the request to the next middleware (or app) in the pipeline,

  4. Upon receiving the response (status, headers, body) from the next middleware (@app), it logs to the console.

  5. Finally, it passes the response to the next middleware in the pipeline. This can be the middleware that called our logging middleware or the web server.

Here’s a diagram that will help you visualize the middleware pipeline.

pipeline.png

The Rack gem provides many such components that you can use. All these components use the same Rack interface.

Here are some middleware components included in the Rack gem.

You can find a complete list here. Pick and choose whatever you like, in any way you wish.