John Topley’s Weblog

Write A Web App In Nine Lines Of Code With Sinatra

One of the trends we've seen on the Web this year has been the proliferation of URL shortening services, to the extent that TinyURL is no longer the default choice. This growth has been driven by the popularity of Twitter with its enforced 140 character message limit and by seamless integration with mobile device Twitter clients such as Tweetie. However, questions over the longevity of these services and the permanence of the links they serve has also led to a new trend of hosting your own. As it seems to be all the rage I thought it would be a fun little exercise to write one for this blog using Sinatra.

You may not have heard of Sinatra, so I’ll give a brief introduction here. To answer the obvious question first, yes, it is named after Ol' Blue Eyes himself. The (non-Frank) Sinatra is a Ruby framework for developing Web applications. Like Merb and Camping it’s one of these fun little Ruby micro-frameworks for when Ruby on Rails is too heavyweight. Which is not to say that Sinatra is a toy that can’t be used for serious work. As it’s so lightweight Sinatra can do some impressive numbers. I've seen 2000 requests processed per second quoted.

A great aspect of all these Ruby frameworks is that they all support the Rack Ruby Web server interface. In practice this means that you get a choice of different Rack-compliant Web servers to run your code, as well as a great selection of Rack middleware for handling all sorts of different tasks. Rack also means that you can compose your Web application of multiple smaller applications. In fact, that’s what I'm doing here because the main blog application is written using Rails and the short URL handler is written using Sinatra.

Probably the hardest part of this project was coming up with a suitably terse and available domain name! In the end I settled on If you hover over a blog post hyperlink on this site then you’ll see a tooltip that shows both the long permalink and the short link.

The general way these URL shorteners work is that the original URL is stored in a database table with an auto-incrementing integer primary key. This key could be used as-is, but is usually Base 36 encoded to give an alternative short representation. Base 36 is used for this token because there are twenty six letters in the Western alphabet and ten digits. There shouldn’t be any need to hold the token itself in the database because it’s cheap to calculate.

I should mention that my implementation is not a general purpose URL shortener. If you visit then you don’t get a text field where you can enter any URL and get a shortened version back. Instead you’ll just get redirected back here. However, adding such functionality would be trivial, so in the traditional manner I’ll leave that as something for the reader to explore.

Rails Application Code

The short blog post links are generated by a method on the Rails application’s Post model class (see code below) that handles the Base 36 encoding. When you click on the short blog post link the request is handled by the Sinatra application which simply performs an HTTP 301 redirect to, passing along the Base 36 token at the end of the URL. A different method within the Post model converts the token back into an integer blog post ID and looks up the corresponding record in the posts table. A second redirect is then made to change the URL to the permanent and canonical version.

class Post < ActiveRecord::Base
  def self.find_by_token(token)
  def token

Sinatra Application Code

Unlike Rails which enforces a standardised directory structure on every application, Sinatra applications can be structured how you like. Sinatra isn’t actually an MVC framework. In fact, it’s entirely possible for a Sinatra app to be contained within a single file! This is jtblog.rb:

require 'sinatra'

get '/?' do
  redirect '', 301

get '/:token' do
  redirect "{params[:token]}", 301

—I don’t think much explanation is needed here because the code is very readable. As you can see, Sinatra gives us a nice little Domain Specific Language (DSL) for the domain of creating Web applications.

There are two handler blocks which both look for HTTP Get requests. The first block simply redirects to this site from, with or without the trailing slash. The second handler block uses Ruby string interpolation to pass along the token URL query parameter to the Rails blog application.

That was seven lines of code, excluding blank lines. The remaining two lines are in the file and are used to Rack up (hence .ru) or bootstrap the application:

require 'jtblog.rb'
run Sinatra::Application

With that done, we’re off to the races!


As you might imagine, it didn’t take me very long to write those nine lines of code. Anyone who’s ever experienced the bad old days of Rails application deployment—now solved thanks to Passenger—will be interested to hear that the deployment process took about half an hour, which included creating an account on Heroku, deploying the application and associating it with a custom domain. I decided to use Heroku rather than Slicehost because I was intrigued by their managed platform and because they let you host an app for free. I'm very impressed so far.

Start Spreading The News

The terseness of the code presented here is not really the point. After all, it’s possible to replicate the Sinatra side of things in one line of configuration using Apache’s mod_rewrite. Of course Sinatra is capable of a great deal more and I know which syntax I find easier to read and write. If you do want to learn more then I can’t recommend the PeepCode Meet Sinatra screencast highly enough. It’s certainly whetted my appetite and given me a few ideas for writing applications (or parts of applications) using Sinatra when previously I would have automatically turned to Rails. Sinatra, I Get a Kick Out of You.


There are 5 comments on this post. Comments are closed.

As it’s so lightweight Sinatra can do some impressive numbers. I've seen 2000 requests processed per second quoted.


  • Jan
  • Feb
  • Mar
  • Apr
  • May
  • Jun
  • Jul
  • Aug
  • Sep
  • Oct
  • Nov
  • Dec

More Archives

Sign In