Making a Simple, Database-Driven Website with Sinatra and Heroku

When I want to make a website, my instinct is to use Rails.  However, sometimes I don’t need everything (or even 50%) of what Rails provides and it feels like a waste for a quick project.  Sinatra may be the simplest web framework ever, but it is also incredibly powerful.  Combined with Heroku, anyone with a little coding experience can have a website running for free in just a few minutes.

There are plenty of good Sinatra tutorials on the web, however I have found that most of them focus on static content.  For those that don’t, I’ve found that very few will also cover how to integrate Sinatra with Heroku Postgres and get your app up and running in the cloud. After having to do so myself earlier this week, I thought it would be useful to others if I posted a quick and to the point article about how to get Sinatra and ActiveRecord up and running on Heroku.  All of the code that follows is available on my GitHub, right here.  In this tutorial you’ll notice that I bold all filenames and shell commands, this is just for readability and you’re free to ignore it.

Note: this tutorial assumes you have necessary software installed such as PostgreSQL, Ruby >= 1.9.3, git, etc.  I also assume you have the ability to install rubygems and that you have the ‘bundler’ gem installed.   I won’t be covering any installation for any necessary software along the way, they will be assumed.

Those who are familiar with Rails probably start projects by typing `rails new projectname` and watching as hundreds of files are generated by RoR and a project springs to life.   With Sinatra there are no such generators, all you’ll need is a good text editor (I’d recommend Sublime Text 2 or vim).

Let’s get started with Sinatra.  First make a new directory for your website, and inside that directory make a file called ‘app.rb‘, a file named ‘config.ru‘ and another called ‘Gemfile‘.  Your ‘app.rb‘ file should look like this:

Your ‘config.ru‘ should look like this:

And your Gemfile should look like this:

app.rb‘ is the meat of the whole project, it’s your entire Sinatra app and will contain much more logic soon.  Gemfile manages your gems, and must be up to date before you can deploy to Heroku.  ‘config.ru‘ helps Heroku run your app, Heroku will consider your Sinatra app to be the same as any other Rack-based ruby app and deploy it by following the instructions in config.ru.

In your Terminal, run ‘bundle install‘ and then ‘ruby app.rb‘.  At this point, you have a fully functioning Sinatra website, and if you navigate to the root of the website you should be greeted with a friendly “Hello, World”.   Easy, right?  Before we move on, this would be a good time to start a new git repository for your project, as we’ll need one later on and it’s always good to have VC if you make a mistake.  Now let’s get to the good stuff.

The first thing we’ll need to do before we can move on is set up your database.  For this tutorial we’ll use PostgreSQL, because it’s what Heroku uses by default.  If you want to continue to run this app locally, you’ll need Postgres installed on your machine.  However, you can just follow along and then run the app on Heroku in the end, there’s no particular reason you’ll need to run anything locally besides sanity checks.   Create a new folder in your project named ‘config‘ and in that folder create the files ‘database.yml‘ and ‘environments.rb‘.  You’ll also need to create a new file in the root of your project called ‘Rakefile‘.  First I’ll show what these files should contain, then I’ll give a brief explanation.  Your ‘database.yml‘ file should look like this:

This file configures your local database.  You don’t actually need it if you don’t plan to run anything locally, but I recommend that you do.   This file assumes that you have postgres installed on your system, running on localhost, and that you have a database called ‘development’.  $PG_USER and $PG_PASS are environment variables you should define in your bash profile that contain your postgres username and password.  You could just put your username and password directly into this file but that’s never good practice.

Your ‘environments.rb‘ file should be as follows:

This file describes how the app will connect to the database in the production (Heroku) and development (local) environments.  Don’t worry about the details, it’s just parsing an environment variable.   You’ll notice that this file mentions something about ActiveRecord, we’ll get to that soon.  Let’s finish up and make a Rakefile like this:

This file configures the rake utility, which we’ll use to run migrations on the database to create the tables we’ll need for this example.

The past few steps have added a few dependencies to our project, so we’ll fix that now.  First, modify your ‘Gemfile‘ to look like this:

and then change your ‘app.rb‘ to look like this:

Now your project has all the dependencies it will need, and should be properly configured to connect to your database.  Run ‘bundle install‘ to install the new gems we’ve required.

Congratulations!  We’ve gotten past all of the hard part.  In fact, if you’re already familiar with Rake and ActiveRecord from past experience you can probably stop now.  Below, we’re just going to create a very simple model and then create some html pages to demonstrate creating and viewing models from the database.  If you just came here to learn how to configure Sinatra and Postgres with ActiveRecord, you should be able to push to Heroku and get going at this point, your app just won’t do anything.

If you decided to stick around, let’s make all that hard work actually good for something.  Create the folder ‘models‘ inside your project, and put a file ‘model.rb‘ inside of it.  The Model class in ‘model.rb‘ will be the simplest AcitveRecord model possible, and it will look like this:

Then to include it in your app, add the line “require ‘./models/model’” to your app.rb file.  Now we need to define the database schema for a Model.  In this case, we’ll keep it as simple as possible, giving it just a single string field called “name”.  At the command line in the root of your project, run the command ‘rake db:create_migration NAME=create_model‘.  That will create a file in your project at the location ‘db/migration/{somedatetime}_create_model.rb‘.  Modify it so it looks like this:

Then run ‘rake db:migrate‘ at the command line.  This will perform the migration and create the table.  If everything was configured correctly above, you shouldn’t get any errors here.

Now that the table is created, we’re ready to start adding and viewing records.  We’re going to need a few more pages for our website.  We’ll need a form for submitting a model and a page for viewing all models that currently exist.  Let’s first create the views for these pages.  Create a folder in your project called ‘views‘ and then create the files ‘views/index.erb‘ and ‘views/models.erb‘.  The erb extension stands for “embedded ruby” and will allow us to create HTML pages from dynamic templates.  The first page, ‘index.erb‘ will be the new homepage of our app and will present us with a form for submitting a new model.  The file should look like this:

If you’re somewhat familiar with HTML forms, you’ll notice that this page contains a form that POSTs its data to the ‘/submit’ endpoint of our website (we’ll create this soon).  The name of the model to be created will be passed as the ‘model[name]‘ parameter.  Now let’s modify ‘app.rb‘ to reflect the changes we just made.  It should now look like this:

The homepage of your app will now render the erb template called ‘index’ (Sinatra assumes the file extension), and we have a new route at ‘/submit’.  This new route only accepts POST requests and will try to make a new model from the HTTP POST parameters.  The ‘save’ method comes from ActiveRecord, and if it returns true we’ll redirect to a new path called ‘/models’, which we haven’t yet created.  Let’s first create the view for the ‘/models’ view, which will display all models we have created so far.  This will be the ‘models.erb‘ file you created earlier.

This is a very simple erb template.  It looks for an instance variable (passed to the view from ‘app.rb‘) called @models which is a list.  For each element of the list, it creates an HTML unordered list item containing the name of the model.  This isn’t a tutorial on erb templating so I won’t get into much of that here, for now just trust what you see above.

The final step in completing our app (locally, at least) is to create the ‘/models‘ route in ‘app.rb‘.  Your final ‘app.rb‘ file should be as follows:

The ‘models’ route collects all Models in the database into the ‘@models’ instance variable and then renders the ‘models.erb’ template.  The ‘Model.all’ call will run the SQL to select all models from your database and return the result set into the @models variable as a list of objects, each one with a ‘name’ field we can access in the template.

So now the app is completely functional, the last step is to deploy to heroku.  The following steps will assume you have the heroku gem installed on your system.  First, run ‘bundle install‘ one last time to make sure your ‘Gemfile.lock‘ is up to date.  Then, commit all of your changes in Git.  Now run ‘heroku create‘, this will magically spin up a new Heroku web instance for you and add it as a ‘git remote’ to your project under the name Heroku.  To deploy, all you need to do is run ‘git push heroku master‘ and wait for the deploy to work.  Then, to migrate the database on Heroku, run ‘heroku run rake db:migrate‘ to create the proper tables.  If you did everything correctly, you should now be able to open your working website by typing ‘heroku open‘ at the command line.

That’s it!  In just a few minutes, you created a dynamic, database-driven app deployed in the cloud.  Now you’re one of the cool kids!  To see what the final app looks like in action (don’t expect anything too pretty), you can click here to access my copy deployed on Heroku.

If you have any questions, leave a comment and I’ll be happy to help.  I hope you found this tutorial useful and it helped you to discover the beautiful power of Sinatra + Heroku.

Edit:

A commenter pointed out that this post was partially inspired by a similar post here.  I had read this tutorial before and forgotten to attribute my source properly, so I’d like to thank Wes Nolte for the environments.rb code and for helping me understand this topic in the first place.  It was not my intention to “rip off” Wes in any way, I took code for this tutorial from my own personal projects which I had forgotten were originally inspired by his post.

18 thoughts on “Making a Simple, Database-Driven Website with Sinatra and Heroku

  1. @Kevin

    I am the original author of this article, and all of the text you see here was written by me. However I did get the environments.rb file from that post a long time ago, and I guess I forgot where I got it from. I’ll edit the article to reflect this, thanks!

  2. Wonderful items from you, man. I have have in mind your stuff prior to and you are just too wonderful.

    I actually like what you have acquired here, really like what you’re stating and the way in which you assert it. You’re making it entertaining
    and you still care for to stay it wise. I cant wait to learn much more from you.
    That is really a terrific website.

  3. Thanks for such a nice post. It is very much helpful and guiding. Looking forward for more post from you.

  4. Thanks for the tutorial. I ran into one small issue getting my app to deploy — the thing that fixed it was adding a Procfile that starts the dyno. I couldn’t get it to run without one.

    The text for my Procfile:
    web: bundle exec ruby app.rb -p $PORT

    That fixed it!

    • Thanks for the feedback! At the time when I wrote this Heroku hadn’t fully moved to the Cedar stack and a Procfile wasn’t needed. It seems it’s now a requirement if you’re using something besides Rails.

  5. Hi,
    this post seemed to be the one that could solve my problem, trying to setup my facebook app locally.

    But while following the steps, I came across a error for which I wasn’t able to figure out a solution.

    Running “rake -T” or any rake command or starting foreman, I was receiving this error:

    —————————————————————————————————————————–
    rake aborted!
    (): found character that cannot start any token while scanning for the next token at line 2 column 1
    /var/lib/gems/1.9.1/gems/sinatra-activerecord-1.2.2/lib/sinatra/activerecord.rb:39:in `database_file=’
    /var/lib/gems/1.9.1/gems/sinatra-1.3.3/lib/sinatra/base.rb:1033:in `set’
    /var/lib/gems/1.9.1/gems/sinatra-activerecord-1.2.2/lib/sinatra/activerecord.rb:50:in `registered’
    /var/lib/gems/1.9.1/gems/sinatra-1.3.3/lib/sinatra/base.rb:1317:in `block in register’
    /var/lib/gems/1.9.1/gems/sinatra-1.3.3/lib/sinatra/base.rb:1315:in `each’
    /var/lib/gems/1.9.1/gems/sinatra-1.3.3/lib/sinatra/base.rb:1315:in `register’
    /var/lib/gems/1.9.1/gems/sinatra-1.3.3/lib/sinatra/base.rb:1648:in `register’
    /var/lib/gems/1.9.1/gems/sinatra-1.3.3/lib/sinatra/base.rb:1688:in `register’
    /var/lib/gems/1.9.1/gems/sinatra-activerecord-1.2.2/lib/sinatra/activerecord.rb:76:in `’
    /var/lib/gems/1.9.1/gems/sinatra-activerecord-1.2.2/lib/sinatra/activerecord.rb:6:in `’
    /home/nitin/facebook_app/guarded-gorge-3234/app.rb:3:in `require’
    /home/nitin/facebook_app/guarded-gorge-3234/app.rb:3:in `’
    /home/nitin/facebook_app/guarded-gorge-3234/Rakefile:1:in `require’
    /home/nitin/facebook_app/guarded-gorge-3234/Rakefile:1:in `’
    /var/lib/gems/1.9.1/gems/rake-10.0.4/lib/rake/rake_module.rb:25:in `load’
    /var/lib/gems/1.9.1/gems/rake-10.0.4/lib/rake/rake_module.rb:25:in `load_rakefile’
    /var/lib/gems/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:589:in `raw_load_rakefile’
    /var/lib/gems/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:89:in `block in load_rakefile’
    /var/lib/gems/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:160:in `standard_exception_handling’
    /var/lib/gems/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:88:in `load_rakefile’
    /var/lib/gems/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:72:in `block in run’
    /var/lib/gems/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:160:in `standard_exception_handling’
    /var/lib/gems/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:70:in `run’
    —————————————————————————————————————————————-

    Could you please help me resolve this.
    Thank you.

  6. Running rake db:migrate failed for me saying “FATAL: database ‘mydb’ does not exist. I got around this by changing line 5 of environments.rb to “db = URI.parse(ENV['DATABASE_URL'] || ‘postgres://localhost/development’)”. While the alternative URI you wrote was ‘postgres://localhost/mydb’, the database on my machine was called ‘development’. This was because you mentioned above that you assumed ‘development’ was the name of our database.

    • Thanks Rahul, your comment was super helpful.

      In my environments file, I had to use full quotations around the URI — “postgres://localhost/development”. Using single quotes didn’t work. I am on Ruby 2.0.0p247.

      Also, this wasn’t clear to me reading the tutorial as a Sinatra/Ruby newcomer, but you have to create a new Postgres database called ‘development’ before you can use the rake commands.

      For anyone curious, I am using Postgres.app from http://postgresapp.com/. SUPER simple to set up. Creating the development db was as easy as “createdb development” in the terminal.

      Thanks Sam for a fabulous tutorial. If you could add these ^ bits to it, it would be perfect for a total beginner. Although I guess I learned some resourceful pieces in the process of figuring out the errors :-)

  7. Pingback: Sinatra Database Error – rake aborted uninitialized constant EN

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s