Ruby Academy · Lesson

Middleware and Rack

The Rack stack.

Lesson 4 of 413 steps

Middleware and Rack is a free Ruby Academy lesson on CoddyKit. This is lesson 4 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the Ruby Academy learning path, and your progress syncs across the web and the CoddyKit app. The Ruby Academy course includes 4 lessons in total.

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
Free to start

Learn Ruby with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
29
Lessons
109

Frequently Asked Questions

Is the “Middleware and Rack” lesson free?

Yes — any 3 lessons of the Ruby Academy learning path, including “Middleware and Rack”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The Ruby Academy course includes 4 lessons in total.

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, so you can start here or from the beginning and move at your own pace. This is lesson 4 of 4.

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