Sinatra
Lightweight microframework. Specialized for API and microservice development, works with minimal configuration.
GitHub Overview
sinatra/sinatra
Classy web-development dressed in a DSL (official / canonical repo)
Topics
Star History
Web Framework
Sinatra
Overview
Sinatra is a lightweight and flexible microframework for Ruby web development. With the catchphrase "little framework that shines," it provides a simple and intuitive DSL (Domain Specific Language) that enables rapid development of web applications and APIs with minimal code.
Details
Sinatra was originally developed by Blake Mizerachy in 2007 and is currently maintained by Tom Christie and many contributors as an open-source microframework. The latest version is 4.1.1, featuring Rack 3 support, lifecycle events (on_start/on_stop), and Puma as the default server.
Key architectural features:
- Microframework Design: Minimal core functionality with high flexibility
- DSL-Centered Development: Intuitive route definitions (
get '/' do ... end
) - Rack Compliance: Compatibility with rich Rack middleware ecosystem
- Modular Design: Choice between classic and modular application styles
- Extension Ecosystem: Rich extensions like sinatra-contrib and rack-protection
Sinatra adopts a philosophy opposite to "convention over configuration," allowing developers to choose only the necessary features. This makes it ideal for API development, prototyping, and microservices construction. It's adopted by companies like Netflix, LinkedIn, and GitHub (partially).
Pros and Cons
Pros
- Extremely Simple: Create web applications with just a few lines of code
- Low Learning Curve: Intuitive DSL that's easy for beginners to understand (Learning curve ★★★★★)
- High Flexibility: Framework-agnostic design allows complete freedom
- Lightweight & High Performance: Minimal overhead with fast execution
- Rack Compatibility: Easy integration of rich Rack middleware
- Rapid Development: Quick construction from prototyping to production
- Modular Support: Well-organized design even for large applications
- Test Support: Efficient test creation with Rack::Test integration
Cons
- Limited Features: No built-in ORM, admin interface, or authentication
- Development Scale Constraints: Less suitable for large full-stack development compared to Rails
- Ecosystem: Limited third-party libraries compared to Rails
- Configuration Burden: Complex configuration when combining extensions
- Security: Security implementation responsibility falls on developers
- Learning Resources: Fewer Japanese learning materials compared to Rails
Key Links
- Sinatra Official Site
- Sinatra Official Documentation
- Sinatra GitHub Repository
- Rack Official Site
- sinatra-contrib Extensions
- Rack-Protection
Code Examples
Hello World
# hello.rb
require 'sinatra'
get '/' do
'Hello World!'
end
get '/hello/:name' do
"Hello #{params['name']}!"
end
# Start server: ruby hello.rb
# Access via browser: http://localhost:4567
Routing and HTTP Methods
require 'sinatra'
# GET request
get '/users' do
"Display user list"
end
# POST request
post '/users' do
"Create user: #{params['name']}"
end
# PUT request
put '/users/:id' do
"Update user ID #{params['id']}"
end
# DELETE request
delete '/users/:id' do
"Delete user ID #{params['id']}"
end
# Regular expression routing
get /\/hello\/(\w+)/ do
"Hello, #{params['captures'].first}!"
end
# Optional parameters
get '/posts/:format?' do
format = params['format'] || 'html'
"Format: #{format}"
end
Templates and Views
require 'sinatra'
# Using ERB templates
get '/' do
erb :index
end
# Using Haml templates
get '/haml' do
haml :index
end
# Inline template
get '/inline' do
erb '<h1>Hello <%= @name %>!</h1>', locals: { name: 'Sinatra' }
end
# Custom layout
get '/custom' do
erb :content, layout: :custom_layout
end
# views/index.erb
# <h1>Welcome to Sinatra!</h1>
# <p>Current time: <%= Time.now %></p>
# views/layout.erb
# <html>
# <body>
# <%= yield %>
# </body>
# </html>
JSON and API Development
require 'sinatra'
require 'json'
# JSON response
get '/api/users' do
content_type :json
users = [
{ id: 1, name: 'John', email: '[email protected]' },
{ id: 2, name: 'Jane', email: '[email protected]' }
]
users.to_json
end
# Receiving JSON data
post '/api/users' do
request.body.rewind
data = JSON.parse(request.body.read)
# User creation logic
new_user = {
id: 3,
name: data['name'],
email: data['email']
}
content_type :json
status 201
new_user.to_json
end
# Error handling
get '/api/users/:id' do
user_id = params['id'].to_i
if user_id <= 0
status 400
{ error: 'Invalid user ID' }.to_json
else
{ id: user_id, name: "User#{user_id}" }.to_json
end
end
Sessions and Middleware
require 'sinatra'
# Enable sessions
enable :sessions
get '/' do
session[:visits] ||= 0
session[:visits] += 1
"Visit count: #{session[:visits]}"
end
# Using Rack middleware
use Rack::Auth::Basic, "Restricted Area" do |username, password|
username == 'admin' && password == 'secret'
end
# Custom middleware
class TimingMiddleware
def initialize(app)
@app = app
end
def call(env)
start_time = Time.now
status, headers, body = @app.call(env)
end_time = Time.now
headers['X-Response-Time'] = (end_time - start_time).to_s
[status, headers, body]
end
end
use TimingMiddleware
Helper Methods and Filters
require 'sinatra'
# Helper method definition
helpers do
def protected!
return if authorized?
headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
halt 401, "Authentication required\n"
end
def authorized?
@auth ||= Rack::Auth::Basic::Request.new(request.env)
@auth.provided? && @auth.basic? && @auth.credentials &&
@auth.credentials == ['admin', 'secret']
end
def format_date(date)
date.strftime('%B %d, %Y')
end
end
# Before filter
before do
@current_time = Time.now
end
# Filter for specific routes only
before '/admin/*' do
protected!
end
# After filter
after do
logger.info "Request completed: #{@current_time}"
end
get '/protected' do
protected!
"Welcome to admin area!"
end
get '/time' do
"Current time: #{format_date(@current_time)}"
end
Modular Style
require 'sinatra/base'
class MyApp < Sinatra::Base
# Configuration
set :sessions, true
set :static, true
set :public_folder, 'public'
# Helpers
helpers do
def current_user
session[:user]
end
end
# Route definitions
get '/' do
erb :index
end
post '/login' do
if params['username'] == 'admin' && params['password'] == 'secret'
session[:user] = params['username']
redirect '/'
else
erb :login, locals: { error: 'Login failed' }
end
end
get '/logout' do
session.clear
redirect '/'
end
# Start application (development)
run! if app_file == $0
end
# config.ru (Rackup file)
# require_relative 'myapp'
# run MyApp
Error Handling and Response Control
require 'sinatra'
# Error handling
not_found do
erb :not_found
end
error do
'An error occurred: ' + env['sinatra.error'].message
end
# Custom error
error 500 do
'Internal server error occurred'
end
# Status code and header setting
get '/teapot' do
status 418
headers 'Content-Type' => 'text/plain'
"I'm a teapot!"
end
# Redirect
get '/old-page' do
redirect '/new-page'
end
# Cache control
get '/cached' do
cache_control :public, :max_age => 3600
"This page is cached for 1 hour"
end
# File sending
get '/download' do
send_file 'files/sample.pdf',
filename: 'sample.pdf',
type: 'application/pdf'
end