Rack provides a minimal, modular, and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the bridge between web servers, web frameworks, and web application into a single method call.
Where is it used?
- Rails (built on Rack)
- Sinatra and Hanami
- Middleware development
What is a Rack-Based Application?
A Rack-based application is any Ruby web application that implements the Rack interface. This means the app must follow Rack’s simple calling convention:
app = Proc.new do |env|
['200', { 'Content-Type' => 'text/html' }, ['Hello, Rack!']]
end
This returns an array of three elements:
- HTTP status code (
'200') - Headers (
{ 'Content-Type' => 'text/html' }) - Response body (
['Hello, Rack!'])
Example: Basic Rack Application
require 'rack'
app = Proc.new do |env|
['200', { 'Content-Type' => 'text/html' }, ['Hello, Rack!']]
end
Rack::Handler::WEBrick.run app, Port: 9292
Run it with:
ruby my_rack_app.rb
Open http://localhost:9292 in your browser.
Does Rails Use Rack?
Yes, Rails uses Rack. Rack serves as the interface between Rails and web servers like Puma or WEBrick.
How Rails Uses Rack
When a request comes in:
- The web server (Puma/WEBrick) receives it.
- The server passes the request to Rack.
- Rack processes the request and sends it through Rails middleware.
- After passing through the middleware stack, Rails’ router (ActionDispatch) decides which controller/action should handle the request.
- The response is generated, sent back through Rack, and returned to the web server.
Check /design_studio/config.ru file in our Rails 8 app is responsible for starting the server.
You can actually run a Rails app using just Rack!
- Create a
config.rufile / use existing one:
require_relative 'config/environment'
run Rails.application
- Run it using Rack:
rackup -p 4343
open http://localhost:4343/products
This runs your Rails app without Puma or WEBrick, proving Rails works via Rack.
Is Rack a Server?
No, Rack is not a server. Instead, Rack is a middleware interface that sits between the web server (like Puma or WEBrick) and your Ruby application (like Rails or Sinatra).
How Does Rack Fit with Web Servers Like Puma and WEBrick?
Puma and WEBrick support Rack by implementing the Rack::Handler interface, allowing them to serve any Rack-based application, such as Rails and Sinatra.
- Puma and WEBrick are not built “on top of” Rackโthey are independent web servers.
- However, they implement
Rack::Handler, which means they support Rack applications. - This allows them to serve Rails, Sinatra, and other Rack-based applications.
The Relationship Between Rack, Web Servers, and Rails
- Rack provides a standard API for handling HTTP requests and responses.
- Web servers (Puma, WEBrick, etc.) implement Rack::Handler so they can run any Rack-based app.
- Rails supports Rack by implementing the Rack interface, allowing it to interact with web servers and middleware.
How Rails Supports Rack
- Rack Middleware: Rails includes middleware components that process requests before they reach controllers.
- Rack Interface: Rails applications can be run using
config.ru, which follows the Rack convention. - Web Server Communication: Rails works with Rack-compatible servers like Puma and WEBrick.
Illustration of How a Request Flows
- The browser sends a request to the server (Puma/WEBrick).
- The server passes the request to Rack.
- Rack processes the request (passing it through middleware).
- Rails handles the request and generates a response.
- The response goes back through Rack and is sent to the server, which then passes it to the browser.
So, while Rack is not a server, it allows web servers to communicate with Ruby web applications like Rails.
Adding Middleware in a Rails 8 App
Middleware is a way to process requests before they reach your Rails application.
How Does Middleware Fit In?
Middleware in Rails is just a Rack application that modifies requests/responses before they reach the main Rails app.
Example: Custom Middleware
Create a new file in app/middleware/my_middleware.rb:
class MyMiddleware
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
body = ["Custom Middleware: "] + body
[status, headers, body]
end
end
Now, add it to Rails in config/application.rb:
config.middleware.use MyMiddleware
Restart your Rails server, and all responses will be prefixed with Custom Middleware:
2 thoughts on “Inside Rails: The Role of Rack ๐ and Middleware ๐”