How to build a super simple API with Ruby on Rails

In this post you will see how building an API with Ruby on Rails can be fast and delightful at the same time. But first, a bit of backstory.

I was building a small app for a client lately and I found myself in the need to rapidly create a prototype before moving on to the serious stuff. I said to myself “let’s try Laravel for this project!” and I thought I could use VueJS too, which is integrated inside Laravel, to build the frontend of the application. I know, having the frontend and the backend inside the same basket is far from ideal but I was in a rush.

Laravel is a great framework but as soon as I started bulding the prototype I felt like I was slowed down. I didn’t progress as fast as I expected.

The very same day I was talking with the CTO of a big company. He told me they were using Ruby on Rails for the backend of their applications and React for the frontend.

I work with React almost daily but I had little to no experience with Rails. I have to admit, I used it in the past and I remember only how messy it was to deal with Gems: every time the server got an update all the applications would break (mostly because we weren’t using RVM), and so our mental health.

Anyway, despite the remembering I decided to give Rails another shot and guess what, it’s been great so far! Rewriting the prototype with Rails and React took me only 3 days and this is why I want to share the happiness with you: in this article you’ll see how to build a simple API with Rails.

React frontend, Rails backend
A prototype of the app I was making (React frontend, Rails backend)

I won’t cover everything though: we will create a super simple API for displaying a list of products. At the end of the article you’ll be able to start from there and add more functionalities to your project.

What you will learn

  1. How to build a simple API with Ruby on Rails 5.1

Requirements

In order to follow along you need Ruby on Rails to be installed onto your system. I suggest using RVM to set up an environment: visit https://rvm.io/ and follow the instructions to install RVM. For the impatient:

gpg2 --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3

curl -sSL https://get.rvm.io | bash -s stable

source ~/.rvm/scripts/rvm

$ rvm install ruby

$ gem install rails

and you are ready to go.

Alongside with Rails you need a basic understanding of the MVC pattern. In the article I’ll assume you know already what a Model or a Controller is.

Configuration and scaffolding

Under RVM, you may want to inizialize the environment before doing anything else. Just run:

source ~/.rvm/scripts/rvm

and then verify that the right version of Ruby has been picked up:

which ruby

if everything is configured correctly you should see the following output:

~/.rvm/rubies/ruby-2.4.0/bin/ruby

At this point we’re ready to create our Rails API. Inside the terminal move to a directory of your choice and generate the project:

rails new rails-simple-api && cd rails-simple-api

Configuring Access-Control-Allow-Origin on Rails

Every time you request a remote resource with Javascript within the browser, the browser itself issues an OPTIONS request before sending any authorization header: those are called Preflighted Requests.

When the API is not configured to respond to such requests you will likely get the error “No ‘Access-Control-Allow-Origin’ header is present on the requested resource”. In other words, you won’t be able to call the API directly. This is why we need to tell Rails to be relaxed about that: the gem rack-cors does exactly that.

In order to use rack-cors you need to update your Gemfile by adding:

gem 'rack-cors', :require => 'rack/cors'

Then update the dependencies with:

bundle install

and finally modify the configuration inside app/config/application.rb. Add the following lines right after config.load_defaults 5.1:

config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource '*', :headers => :any, :methods => [:get, :post, :options, :patch]
  end
end

and we are done! Now your Rails API will be able to serve the requests without any error.

Setting up the routes

Setting up the API routes in Rails is easy as typing one line of code. That’s it: you just need to configure your resources in config/routes.rb and Rails will take care of the rest.

A resource represents a collection of objects: if you want to build a store for example, you’ll need a Products resource to start with. That’s exactly what we will do in our example API. Open up app/config/routes.rb and add your first resource:

Rails.application.routes.draw do
  resources :products
end

now if you type:

rails routes

you will se the following output:

      Prefix Verb   URI Pattern                  Controller#Action
    products GET    /products(.:format)          products#index
             POST   /products(.:format)          products#create
 new_product GET    /products/new(.:format)      products#new
edit_product GET    /products/:id/edit(.:format) products#edit
     product GET    /products/:id(.:format)      products#show
             PATCH  /products/:id(.:format)      products#update
             PUT    /products/:id(.:format)      products#update
             DELETE /products/:id(.:format)      products#destroy

That’s kind of magic isn’t it? It takes only one line of code to have all the routes automagically generated for you. The routes come with the corresponding CRUD operation. In fact every resource you declare in Rails can be created, updated, and destroyed by the means of a CRUD operation.

Generating the model and seeding the database

Now that we have our routes and our products resource, we need to create a model. Again, all you need to do is type one command:

rails generate model Product name:text price:integer

Rails will create the Product model in app/models/product.rbalongside the corresponding migration inside app/db/migrate/..._create_products.rb

Now, I’m pretty sure you have already spotted the error here. Inside the model I used the integer type for storing the price of my products. That’s not really a best practice but for the scope of this post I’d like to keep things simple. Either way, you may want to store prices inside your database as decimals.

Destroy the previous model with:

rails destroy model Product

and re-create it with:

rails generate model Product name:text price:decimal{15,2}

This will create a new Product model where the price of every product will be stored as a decimal with a precision of 15 and a scale of 2.

We have a model now but our database is empty! What should we do? We need to create some fake data! Rails has an interesting feature: database seeding (Laravel has something similar too).

If you snitch inside the app/db directory you will see the seeds.rb file. Open up the file and add the following lines:

products = Product.create([
    {name: 'Giant Reign', price: 2560.55},
    {name: 'Santa Cruz Nomad', price: 7510.55},
    {name: 'Yeti SB5', price: 8715.55},
])

Those bikes are quite expensive aren’t they? Of course you are free to add whichever type of product you prefer, but since I’m a biker I went with bikes!

We’re ready to seed our database but first we need to commit our migration. That can be done with a one liner:

rails db:migrate db:seed

The db:migrate command will create the table products in our database and the db:seed command will seed our table with some sample data.

Generating our first controller

One last piece is missing: a controller for our product model. The controller is the C in MVC. To put it simply, the controller is responsible for intepreting the request, fetching the requested data and sending an appropriate output back to the user.

If you take a look again at the output of rails routesyou’ll see that the user should visit /products in order to see all our products:

  Prefix Verb   URI Pattern                  Controller#Action
products GET    /products(.:format)          products#index

Rails explicitly tells us that if you want to see all the products, a controller named products should exists and a corresponding action named index should be defined inside the controller as well. An action is simply a Ruby method which fetches the data from the database and returns it to the user.

To create a controller all you need to do is run:

rails generate controller Products

Rails will create the corresponding controller inside app/controllers/products_controller.rb

All we need to do now is define the index method. Open up app/controllers/products_controller.rb and add the action:

class ProductsController < ApplicationController
    def index
        @products = Product.all()
        render json: { status: 200,
                      data: @products }
    end
end

As you can see, the action index is a method. It retrieves the products list from the database, stores said list inside an instance variable, and finally it renders the data as a json object. Also, a status code is attached to the response.

You won’t believe it but our API is ready to go.

Let’s recap what we have done so far:

  1.  We have created a products resource
  2. Rails has automagically generated the routes for the products resource
  3. We have created a Product model
  4. We have created a Products controller alongside the index action

Now it’s time to test our API!

Making use of our Rails API

Our API is ready and we can start by fetching the products list. You have many options:

  1. using the JSON formatter extension on Chrome
  2. using Curl
  3. using Postman
  4. using a frontend library (React, VueJs)

We won’t cover the point 4 in this post, we just want instant feedback about whether our API is working or not.

First, start Puma with:

rails server

the server will start with the following ouput:

=> Booting Puma
=> Rails 5.1.2 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.9.1 (ruby 2.4.0-p0), codename: Private Caller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

Now, If you have JSON formatter installed, head straight over http://localhost:3000/products. You should see the following output:

Rails Api
JSON Formatter in action

It works! If you see the same output, congrats, that means your API is working correctly!

Besides querying the API with the browser, which will give you instant feedback but it’s far from practical, there are a lot of tools out there. One of them is Postman.

To test your API with Postman just enter http://localhost:3000/products inside the Url field and clic Send. You should see the following output:

Rails Api, testing with Postman
Postman in action

Even Postman approves! Our Rails API is indeed working!

Takeaways

Picking a web framework over another it’s a matter of personal preferences/requirements (sometimes). But when it comes to prototyping or building the actual product pick whichever framework makes you write code faster. For me, Rails has been the one.

Why Rails?

  1.  rapid prototyping
  2. the DB is configured out of the box (Sqlite to start with)
  3. a pleasure to work with
  4. coding in Ruby feels like you are writing plain English

Last but not least, the documentation is fantastic: Rails Guide

Where to go from here

Extending the API. I’ve just covered the basics here. You know how to create a model, and how to create a controller alongside an index action, but there is more: try to extend the API by supporting the POST, PUT, and DELETE operations.

Adding some tests. The API lacks of tests. After adding your new actions inside the controller, try to write some tests. Hint: look inside app/test

Thanks for reading!

Valentino Gagliardi

Valentino Gagliardi

Web Developer & IT Consultant, with over 10 year of experience, I'm here to help you developing your next idea.
Valentino Gagliardi

7 Replies to “How to build a super simple API with Ruby on Rails”

  1. Thanks for your post! Very nice article. It was interesting to read. As I see you are really experienced, developer and I suppose you can create your own startup/company. Good luck!

  2. change generate model Product to {‘15,2’} avoiding shell problems

    rails generate model Product name:text price:decimal{‘15,2’}

Leave a Reply

Your email address will not be published. Required fields are marked *