How to set up Sinatra with ActiveRecord
The fourth post in a series on building a microservice with Sinatra and ActiveRecord and deploying it with Docker, in which we add ActiceRecord to the Sinatra app.
In my series Learning Docker, I document my steps to use Sinatra with Docker. Part of this journey is to set up Sinatra with a database, and because I love ActiveRecord I want to use it as well. In this post, I am going to show you how to do that.
Introduction
In case you are not familiar with my series and its goals, I’ll quickly give you an overview:
For one of my projects I decided to take a look at Docker, and build my application using services. Most of these I want to build as JSON APIs with Sinatra, since I like how lightweight yet extensible it is. The first parts of the series dealt mostly with setting up a very basic Sinatra app and getting Docker to run.
If you want to inspect the source code of the application as it is now, follow this link: docker-sinatra-api@0.2.0
Pulling in the dependencies
The first step to get Sinatra running with ActiveRecord is define and install the required dependencies.
Let’s start by adding the gem pg
to our Gemfile
. This is the database adapter for PostgreSQL, which are are going to use in production. We are also adding sqlite3
for the development and test environment.
group :production do
# Use Postgresql for ActiveRecord
gem 'pg'
end
group :development, :test do
# Use SQLite for ActiveRecord
gem 'sqlite3'
end
Next, we need to configure our application. As I mentioned, I am a big fan of ActiveRecord, and want to use it to handle my database connection for me. While we are at it, we are also adding Rake. ActiveRecord’s gem includes all the tasks you might be used to from Ruby on Rails, and with Rake you can use them in just the same way as in Ruby on Rails:
# Use ActiveRecord as the ORM
gem 'sinatra-activerecord', '~> 2.0'
# Use rake to execute ActiveRecord's tasks
gem 'rake'
Now that we have the right tools in place, let’s look at the configuration.
Configuring the database
To make everything as comfortable as possible for you, we are going to follow the conventions of Ruby on Rails and put the database configuration in config/database.yml
. With SQLite for the development and testenvironment, and PostgreSQL for production, our configuration file looks like this:
default: &default
adapter: sqlite3
pool: 5
timeout: 5000
development:
<<: *default
database: db/development.sqlite3
test:
<<: *default
database: db/test.sqlite3
production:
adapter: postgresql
encoding: unicode
pool: 5
host: <%= ENV['DATABASE_HOST'] || 'db' %>
database: <%= ENV['DATABASE_NAME'] || 'sinatra' %>
username: <%= ENV['DATABASE_USER'] || 'sinatra' %>
password: <%= ENV['DATABASE_PASSWORD'] || 'sinatra' %>
The production environment is configured via environment variables, what allows us to keep our sensitive passwords out of the code base and out of version control. In case the variables are not set, we fall back to default values that are also going to configure in Docker as the credentials for the database. This makes it easy to use the application with Docker for development purposes.
Configuring the application
The next step is to make ActiveRecord available to our application. While we have installed the gem and provided its configuration, we are not yet able to use it within our code.
There are two things we need to do:
- Add a
require
statement to pull in ActiveRecord - Tell it where to find its configuration
Look at the beginning of our app.rb
file to see how this is done:
require 'sinatra'
require 'sinatra/json'
require 'sinatra/activerecord'
set :database_file, 'config/database.yml'
This is all that is necessary to get ActiveRecord working with a Sinatra application. You can now define your classes just like you would in Ruby on Rails.
See the next section for an example.
Using ActiveRecord
Let’s say we want to manage Resources. First, let’s create a class for them. Put it in app.rb
, above the first get
statement.
class Resource < ActiveRecord::Base
validates :name, presence: true, uniqueness: true
end
Now that we have our Resource class, we can implement the CRUD operationsfor it. We start by extending the #index
action:
get '/' do
json Resource.select('id', 'name').all
end
Next up is the #show
action:
get '/:id' do
resource = Resource.find_by_id(params[:id])
if resource
halt 206, json resource
else
halt 404
end
end
Followed by #create
:
post '/' do
resource = Resource.create(params)
if resource
json resource
else
halt 500
end
end
#update
:
patch '/:id' do
resource = Resource.find_by_id(params[:id])
if resource
resource.update(params)
else
halt 404
end
end
And #delete
:
delete '/:id' do
resource = Resource.find_by_id(params[:id])
if resource
resource.destroy
else
halt 404
end
end
These are very basic implementations, just to show you that everything works as expected. For an application that is aimed for production, I strongly advice you to do a more robust implementation than this.
Creating the table
Before we can do anything with this newly written code, we need to create the table for our Resource class. One of the nicest things about ActiveRecord is the support for migrations, which define changes you make to the database as code that can be run in a reproducible way. So let’s create a migration. Remember Rake?
$ rake db:create_migration NAME=create_resources
The output of this command is the file path to your newly created migration. Open it, and add the following instructions:
class CreateResources < ActiveRecord::Migration
def change
create_table :resources do |t|
t.string :name, null: false, default: ''
t.timestamps, null: false
end
add_index :resources, :name, unique: true
end
end
Just like you would in Ruby on Rails, you can run this migration with the following command:
$ rake db:migrate
The migration created the table resources
for you, with an implicit field id
, the specified field name
and timestamps containing the times of creation and the last update.
You can now start the application and browse to its URL. The output from the #index
method should be []
, since we don’t have created any resources yet.
Congratulations! You have successfully combined Sinatra with ActiveRecord.
Summary
Setting up Sinatra with ActiveRecord is not difficult. We installed the necessary dependencies via our Gemfile
, configured the database connection in config/database.yml
, created and ran a migration with Rake and finally added a little bit of configuration to our app.rb
to make it use ActiveRecord.
The provided code examples are very basic implementations of what is possible, and I strongly recommend that you build a more solid API if you want to try Sinatra. Two things that immediately come to mind when thinking about areas to improve are authentication and pagination. Right now, everyone can create and delete resources like he wants. That is almost never what you want, though.
If you want to study the code more, have a look at the GitHub repository for my series on Learning Docker. The following version of the code is the result of this post:
You might want to check out the series as well, since it described how to pack everything we did today into a Docker container and make it portable. Start with the first post:
If you have any questions or suggestions for improvement, feel free to leave a comment below. I would love to hear from you.
Hope to see you in the next post!
Jan David