Technical writings of Shkrt



Building API with Roda and Sequel

Both Roda and Sequel are authored by Jeremy Evans. First is described as ‘Routing Tree Web Toolkit’ and offers web framework experience without database persistence layer, which is, for example, can be provided by aforementioned Sequel. If you’re coming from Rails world, you might have been used to ActiveRecord as database ORM, and taking a glance at Sequel is a must for you. Otherwise, you perhaps have used Sequel in Sinatra projects. It’s also worth noting that both projects have zero issues on GitHub.

Let’s start simple. Jeremy Evans has carefully prepared repo roda-sequel-stack which contains basically everything we need to have simplest web app up and running. I’m repeating first steps from the repo docs:

$ git clone
$ mv roda-sequel-stack my_app
$ cd my_app
$ rake setup[MyApp]

Then you should perform database creation manually, or create simple rake task for it:

desc "database-related tasks"
namespace :db do
  desc "create database"
  task :create do"creating database")
    %x( createdb -E UTF8 -T template2 -U postgres --password -h localhost roda_app_development )"successfully created database")

Database connection string is meant to be stored in .env file:

ENV['DATABASE_URL'] = "postgres://postgres:qwerty@localhost/my_app_development

This will be automatically picked up at application start.

To start a blank application, you can just add the following to and then execute rackup from console:

require "roda"

Roda.route { "Hello world!" }

But suggested setup from roda-sequel-stack repository proposes better way to organize code, and we move framework code to app.rb, and leave with following code

Now we can add first routes into app.rb

# app.rb
require_relative "models"
require "roda"

class App < Roda
  plugin :json, classes: [Array, Hash, Sequel::Model]

  route do |r| 'books' do
      @books = Book.all

Of course, this piece of code contains not only routes but also some database querying and serializing logic. Some important points:

Of course, this would not work yet, because we have neither Book model, nor books table. So, let’s create them.

# migrate/001_create_books.rb
Sequel.migration do
  up do
    create_table(:books) do
      primary_key :id
      String :title, null: false
      String :image_data, text: true
      Integer :release_year

  down do

This is pretty straightforward and does not need explanation, I think.

The model does not contain much code by now:

# models/book.rb
class Book < Sequel::Model
  plugin :json_serializer

Run rackup now, make GET request to our app’s /books URL, and you should see an empty response. If you don’t like it empty, of course, Book records can be created, for example, by running rake dev_irb

$ curl localhost:9292/books
$ {"books":[[2,"Book title",null,2000]]

Let’s go further and implement other CRUD actions.

# app.rb
plugin :json_parser
plugin :all_verbs
plugin :halt

We have added three new plugins to app.

route do |r|
  # /books used to match get and post verbs, to provide respectively
  # books index and and book creation 'books' do
    r.get do
      page = r.params[:page] || 1
      { books: Book.paginate(page, 20).map(&:to_json) }
    end do
      @book = Book.create(book_params(r))
      { book: @book.to_json }
  end 'book', Integer do |book_id|
    # book/:id used to match get, put and delete request, to provide
    # getting book by id, updating and deleting book
    @book = Book[book_id]
    # use halt to return 404 without evaluating rest of the block
    r.halt(404) unless @book

    r.get do
      { book: @book.to_json }

    r.put do
      { book: @book.to_json }

    r.delete do
      response.status = 204


def book_params(r)
  { release_year: r.params['release_year'], image_data: r.params['image_data'], title: r.params['title'] }

book_params method is a shortcut to DRY out parameters used for record changing. Also, you may have noticed pagination was added at line 7 This is not yet implemented in the model, so let’s add it:

# models/book.rb
class Book < Sequel::Model
  class << self
    def paginate(page_no, page_size)
      ds = DB[:books]
      ds.extension(:pagination).paginate(page_no, page_size)

pagination is a Sequel extension, providing offset + limit pagination.

Now we have a very basic API application, that gives us access to one of our application’s tables via very trivial CRUD interface. We’ve almost built it from scratch, and though the sample setup from Jeremy Evans repo has been used, it is very simple and we can track every performed action and break down an application into parts. This looks very advantageous when compared to frameworks providing some kind of generators, where you’re not controlling each aspect of app building. Also, there are no tons of middlewares, bloating and slowing down the request-response cycle (though you can feel suspicious for plugins, they’re nothing compared to Rails middlewares). In my opinion, the best option for one to try roda-sequel stack without going all the way without Rails is to plug Roda API to Rails app via Rails’s config/routes.rb file. If you had not known, Rails is capable of mounting any Rack app within its routing tree, so Roda, being Rack-based, is perfectly fit for such use-cases. Using Sequel instead of ActiveRecord is also an option, but this would be a subject of another article, I hope.

[ ruby  roda  sequel  ]