0Pricing
Ruby Academy · Lesson

Middleware and Rack

The Rack stack.

Middleware and Rack is a free Ruby Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Ruby Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Is Rack?

Rack is the minimal interface between Ruby web servers and frameworks. Sinatra, Rails, and others all run on Rack.

  • Standardizes how requests and responses flow
  • Enables reusable middleware
  • A Rack app is anything that responds to call(env)
app = lambda do |env|
  [200, { 'Content-Type' => 'text/plain' }, ['Hello Rack']]
end

puts app.call({}).inspect

The Rack Contract

A Rack app's call must return a three element array:

  • Status integer (e.g. 200)
  • Headers hash
  • Body that responds to each (usually an array of strings)
status, headers, body = 200, { 'Content-Type' => 'text/html' }, ['<h1>Hi</h1>']
puts status
puts headers.inspect
body.each { |chunk| puts chunk }

The env Hash

The env argument is a hash describing the request.

  • REQUEST_METHOD like GET or POST
  • PATH_INFO the requested path
  • QUERY_STRING raw query parameters
env = { 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/home' }
puts "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}"

What Is Middleware?

Middleware wraps an app to add behavior before or after the request reaches it.

  • Logging, authentication, compression
  • Each middleware also responds to call(env)
  • It calls the next app in the chain
class Logger
  def initialize(app)
    @app = app
  end

  def call(env)
    puts "Request: #{env['PATH_INFO']}"
    @app.call(env)
  end
end

Building a Middleware Class

Middleware stores the wrapped app and delegates to it in call.

  • Constructor receives the inner app
  • call can modify env, then forward
  • It can also alter the response on the way back
class UpcaseBody
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
    new_body = body.map(&:upcase)
    [status, headers, new_body]
  end
end

puts 'Middleware defined'

The Middleware Stack

Middleware forms a stack. The request flows down to the app, the response flows back up.

  • Outer middleware runs first on the way in
  • Outer middleware runs last on the way out
inner = lambda { |env| [200, {}, ['core']] }

class Wrap
  def initialize(app); @app = app; end
  def call(env)
    s, h, b = @app.call(env)
    [s, h, b.map { |x| "[#{x}]" }]
  end
end

puts Wrap.new(inner).call({}).last.inspect

Using Middleware in Sinatra

Add middleware to a Sinatra app with use.

  • use Rack::Logger
  • Order of use calls defines the stack order
require 'sinatra'

use Rack::Logger

get '/' do
  'Logged request'
end

config.ru and rackup

A config.ru file boots a Rack app with rackup or Puma.

  • run sets the final app
  • use adds middleware before it
# config.ru
require './app'
use Rack::CommonLogger
run Sinatra::Application

Common Built-in Middleware

Rack ships with useful middleware.

  • Rack::CommonLogger request logs
  • Rack::Deflater gzip compression
  • Rack::Static serve files
  • Rack::Session::Cookie sessions
# In config.ru
# use Rack::Deflater
# use Rack::Static, urls: ['/assets'], root: 'public'
puts 'See comments for usage'

Modifying the Response

Middleware often adds response headers after the inner app runs.

  • Capture the response triple
  • Mutate headers, then return it
class AddHeader
  def initialize(app); @app = app; end
  def call(env)
    status, headers, body = @app.call(env)
    headers['X-Powered-By'] = 'Ruby'
    [status, headers, body]
  end
end

app = AddHeader.new(lambda { |e| [200, {}, ['ok']] })
puts app.call({})[1].inspect

Short Circuiting Requests

Middleware can halt a request without calling the inner app.

  • Useful for auth gates or rate limits
  • Return a response triple directly
class Auth
  def initialize(app); @app = app; end
  def call(env)
    return [401, {}, ['Unauthorized']] unless env['token']
    @app.call(env)
  end
end

app = Auth.new(lambda { |e| [200, {}, ['ok']] })
puts app.call({})[0]

Quick Check

Verify your Rack understanding.

Recap

You learned the Rack stack:

  • Rack is the common interface beneath Ruby web frameworks
  • A Rack app responds to call(env) and returns [status, headers, body]
  • Middleware wraps an app to add cross cutting behavior
  • Use use in Sinatra or config.ru to compose the stack
  • Middleware can modify responses or short circuit requests

Frequently asked questions

Is the “Middleware and Rack” lesson free?

Yes — the full text of “Middleware and Rack” is free to read here on the web, and the Ruby Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Ruby Academy course, upgrade to CoddyKit PRO.

What will I learn in “Middleware and Rack”?

The Rack stack. You practise Ruby Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Ruby Academy?

No prior experience is required. Ruby Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Middleware and Rack” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Ruby Academy lesson?

Yes. Every Ruby Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Sinatra Basics
  2. Routes and Params
  3. JSON APIs
  4. Middleware and Rack
← Back to Ruby Academy