The "Black Box" Between the Browser and Your Code
When you are learning Rails, you usually think the flow works like this:
- User clicks a link.
- Magic happens.
- Your
PostsControllerruns.
Today, we are going to look at the Magic. That magic is called Middleware.
Understanding Middleware is the difference between a Junior dev who thinks "Rails just does that" and a Senior dev who knows exactly how Rails does that.
The Mental Model: The Onion
The best way to visualize Middleware is the Onion Architecture.
Imagine your application logic (your Controller) is the core of the onion. The user's request is a needle trying to reach the center.
- The request pierces the outer skin (Middleware 1).
- It goes through the next layer (Middleware 2).
- It reaches the center (Your Controller).
- Crucially: The response then has to travel back out through Middleware 2, then Middleware 1, before leaving the server.
This means Middleware can modify the Request coming in, AND the Response going out.
What actually lives there?
If you run bin/rails middleware in your terminal right now, you will see a list of about 20-30 items. Rails is essentially just a big stack of middleware.
Here are the common "layers" you use every day without knowing it:
-
ActionDispatch::Static: "Is this request asking forlogo.png? If yes, serve the file and stop here. Don't bother the controller." -
Rack::Cors: "Is this website allowed to talk to our API? No? Block it." -
ActionDispatch::Cookies: "Take this messy HTTP header and turn it into a nicecookies[:user_id]hash for the controller." -
Warden(if using Devise): "Is this user logged in? If not, redirect them before they even touch the application code."
How to Build Your Own (The Rack Protocol)
In Ruby, the standard for web apps is Rack. A middleware is just a Ruby class that follows a specific set of rules.
Here is a simple middleware that measures how long a request takes.
# app/middleware/timer_middleware.rb
class TimerMiddleware
def initialize(app)
@app = app # The "next" layer of the onion
end
def call(env)
# 1. THE "ENTER" PHASE (Request coming in)
start_time = Time.now
puts "Request started..."
# 2. PASS TO THE NEXT LAYER
# This recursively calls the next middleware, all the way to the controller
status, headers, body = @app.call(env)
# 3. THE "EXIT" PHASE (Response going out)
duration = Time.now - start_time
puts "Request finished in #{duration} seconds"
# 4. Return the response to the previous layer
[status, headers, body]
end
end
To use it in Rails, you add one line to your config:
# config/application.rb
config.middleware.use TimerMiddleware
Now, every single request to your server will be timed, and your controllers don't even know this code exists.
Why Should You Care?
You might be thinking, "This is cool, but I just write controllers."
Here is why understanding Middleware makes you a better developer:
1. Performance Debugging
If your app is slow, but your Controller code looks fast, the problem is likely Middleware. Maybe your Session store is slow, or a bad CORS configuration is hanging the request. Knowing the stack lets you debug outside the controller.
2. Separation of Concerns
Let's say you want to ban all IP addresses from a specific country.
- Junior Dev: Adds a
before_action :check_iptoApplicationController. This is slow because the request has to go through the entire Rails boot process just to be rejected. - Senior Dev: Writes a Middleware. The request is rejected at the very edge of the application, saving memory and CPU cycles.
3. API Keys and Auth
If you are building an API, checking for a Bearer token in every controller is tedious. A Middleware can check the header, validate the token, and define env['current_user'] before your code ever runs.
Summary
Middleware isn't scary. Itβs just a chain of passing the baton.
- Request -> Middleware A -> Middleware B -> Controller
- Response <- Middleware A <- Middleware B <- Controller
Next time you see a "CORS Error" or a "Session Cookie" issue, remember: Check the Onion.
Have you ever written a custom middleware to solve a weird problem? Tell me about it in the comments! π
Top comments (0)