Deploying a Sinatra App to Heroku
--
For this project, we had to build an application employing the MVC (model-view-controller) design pattern and CRUD (create, read, update, destroy) paradigm, using the Sinatra DSL (domain specific language). The app also had to be a Content Management System (CMS), that is, a system that tracks something.
After a conversation with a friend, I decided to create an application for tracking publication advances. Advances are payments made upfront to authors, as an advance against royalties that will ultimately be received on the publication and sales of a book. These payments are an area of mystery; much like salaries, neither the author nor the publisher typically share the amount of an advance received. So how do you know if what you agreed to is fair? How do you know how much you’re making relative to a peer? Did they receive more on their first book than on your third book? Why?
The lack of readily available information can mask and contribute to inequities, as the #PublishingPaidMe twitter campaign exposed. This project aims to address that gap. It serves as a prototype for a site where authors can share the advances they received for the books they’ve published, and see other authors and advances, thus increasing transparency and, hopefully, reducing inequity.
Building the App
Technically, building the MVC and CRUD frameworks was straightforward. With MVC, you have: 1) models — classes that represent real world things, such as authors and books, and connect with your database via ActiveRecord; 2) views — the content that users see when they access your app; and 3) controllers — classes that define routes and manage the logic connecting the user and the models.
CRUD simply represents Create, Read, Update, and Delete, i.e., the actions that an app needs to be able to perform on data. Authors can create books, read books (pun intended!), update books, and delete books. Authors can also create, read, update, and delete their own accounts. These functions are all represented as routes, connected to views, in the application controller.
Writing the models, controllers, and views and getting the application to run on my machine via the shotgun
gem was manageable. Deploying the app to Heroku was an entirely different story.
Deploying to Heroku
I had used Heroku many years ago for small JavaScript applications I had completed through FreeCodeCamp, but had never used it for the more complex applications I have been writing recently.
I tried to follow Heroku’s guide for Ruby, but almost immediately ran into problems. To the best of my knowledge, the essential issue was that Heroku’s default stack, heroku-20, did not support my version of Ruby (2.6; more on this below). Trying to fix the errors led me down a rabbit hole of frustration.
Rather than waste even more hours troubleshooting the walk-through, I decided to restart the setup process with my own application. In the hope that it assists you with your own project, here are the steps I took to deploy to Heroku.
Set up Heroku:
- Create a Heroku account.
- Install the Heroku Command Line Interface (CLI).
- In the terminal, run
heroku login
. It will take you to the browser to authenticate your login information.
Prepare the application for deployment (assuming you already have a Ruby/Sinatra app that runs locally, and that it uses Git for version management):
- Require PostgreSQL in your Gemfile (
gem “pg”
). While your app is probably written with a SQLite3 database, Heroku requires a database like PostgreSQL. To add it to my Gemfile, I separated the development group (the gems I used to develop the app) from the production group (used to deploy to Heroku):
group :development do
gem “sqlite3”
gem “pry”
gem “tux”
gem “faker”
endgroup :production do
gem “pg”
end
- Ensure that you specify the Ruby version you are using in your Gemfile. In my application, I added
ruby “2.6.21”
right below thesource
line of the Gemfile. Runbundle install
to updateGemfile.lock
. - Add a
database.yml
file in the config folder. This file specifies how to connect to your database. Mine read as follows (you can learn more about how Heroku handles database connections with Ruby here).
development:
adapter: sqlite3
encoding: unicode
database: db/development.db
host: localhost
pool: 5production:
adapter: postgresql
encoding: unicode
database: production
host: <%= ENV[‘DATABASE_HOST’] %>
pool: 5
- In the
config/environment.rb
file, you must direct your application to use thedatabase.yml
file to establish the database connection. So, instead ofset :database, {adapter: “sqlite3”, database: “db/#{ENV[“SINATRA_ENV”]}.sqlite”}
, useset :database_file, “./database.yml”
. - Add a
Procfile
in your root directory, without a file extension. This file specifies the commands that the app, on Heroku, will execute when it starts. It should include the code,web: bundle exec rackup config.ru -p $PORT
.
Set up deployment:
- In the terminal, run
heroku create
. If you are running Ruby 2.6 (like me!), runHeroku create --stack heroku-18
(Ruby 2.6 isn’t supported on the default stack, heroku-20). This command creates a Heroku-hosted Git repo. - Check that the Procfile is working by running the file’s command in the terminal. You’ll have to fill out $PORT with a port number (e.g.,
bundle exec rackup -p 9292 config.ru
). If you don’t see any errors and are able to view the app in your browser on the port specified, you’re good! You should still be able to run the app withshotgun
, and it should work as normal. - Provision a Heroku Postgres database by running
heroku addons:create heroku-postgresql:hobby-dev
(hobby-dev
is the free version). - Set up Postgres on your machine (see here for a more detailed guide), then export the DATABASE_URL environment variable to ensure your local environment and Heroku environment are connected. Run
export DATABASE_URL=postgres://$(whoami)
. - When you’re ready, run
git push heroku main
. This should deploy the app. - Check it out with
heroku open
.
If you run into errors (so many errors!), don’t despair. Take a deep breath, read the error codes for guidance, and google anything you don’t know. Here are a few other tips that helped me:
- If you need to run a process that would normally employ rake, add
heroku run
before the command. For example, to run a migration on Heroku, runheroku run rake db:migrate
. - Try restarting Heroku dynos:
heroku restart
. - Try restarting the terminal.
To learn more about my project, visit my GitHub repo or check out this five-minute walk-through. You can view the Heroku demo here.