For Developers

Building a Rest-Like API Micro-Framework for Ruby

Build REST-like API Micro-framework in Ruby

Ruby is a dynamically typed language that gained traction in the mid-2000s by starting with web development. However, its low popularity with single-page apps and modern APIs calls for the need for third-party integration. Moreover, we do so to extend our applications with API endpoints in order to support JavaScript - heavy rich internet clients or Android/ iPhone apps.

One of the simplest ways to accomplish the above task is to expose REST APIs for consumption.

In this article, we will walk you through the best approach to designing and implementing a REST API in a task management web application. In the end, we will also cover some of the best practices that can ensure the maintainability of the code.

Building a REST-like API micro-framework for Ruby using Grape

Introduction to Ruby Grape

Ruby Grape is a REST-like API framework for Ruby that runs on Rack. It complements existing web application frameworks, such as Rails and Sinatra, by providing a simple domain-specific language (DSL) to develop RESTful APIs easily. It includes built-in support for common conventions, including sub-domain/prefix restriction, content negotiation, multiple formats, versioning, and much more.

Phase 1: Getting started with Grape

Before we get to phase one of how to start with Grape, here are some points on the method we will be focussing on.

We will focus on the “use case”, which is an application that is capable of capturing and reviewing pair programming sessions. It will be written for iOS in ObjectiveC itself and will be required to communicate with the backend service to store and retrieve the data. Our main goal is to create a robust and secure backend service that supports a JSON API.

This API will

  • Support methods for logging into the system

  • Support methods for querying pair programming session reviews

  • Will provide a facility for submitting pair programming reviews to include in the database

Note: The software development approach to be used is test-driven development to help ensure the deterministic behavior of our API.

Now, let’s get started with the main part, i.e. building a REST-like API.

Step 1: We will start by laying down the foundation for our backend API. To do so, we will need to create a new rails application as follows.

rails new toptal_grape_blog

Code source

Step 2: Install the RSpec into our gemfile by adding rspec-rails.


group :development, :test do
  gem 'rspec-rails', '~> 3.2'
end

Code source

Step 3: Run the same with the following code.

rails generate rspec:install

Code source

Step 4: The next step includes adding the testing frameworks. We will use RSpec which is the well-known testing framework for testing in the RubyonRails community. It will yield both positive and negative results. The negative specs will dictate issues like how the API endpoint behaves when some parameters are incorrect or missing. The positive specs include cases where the API is invoked correctly.

Note: You can also leverage other open source software for the testing framework such as:

- Devise: It is a flexible authentication solution for rails that is based on Warden.

- Factory_girl_rails: It offers rails integration for factory_girl which is a library used for setting up Ruby objects as test data.

We will be using the above two in this project.

Step 5: Add devise and factory_girl_rails into our gemfile.


...
gem 'devise'
...

group :development, :test do
...

  gem 'factory_girl_rails', '~> 4.5'
...
end

Code source

Step 6: Initialize the device gem in the generated user model and add it to the same to enable the user class to be used for authentication as follows.


rails g model user
rails generate devise:install
rails generate devise user

Code source

Step 7: To use the abbreviated version of user creation in our specs, include the factory_girl syntax method in our rails_helper.rb file.


RSpec.configure do |config|
  config.include FactoryGirl::Syntax::Methods

Code source

Step 8: Add and install the Ruby grape gem to our DSL.


gem 'grape'
bundle

Code source

Phase 2: Including user login use case and Spec

Step 1: In order to support a basic login capability, we will create a skeleton for our login_spec.

Assumption: We ‌assume that a valid login request will include a registered email address and password.

Here’s how you can proceed on the same.


require 'rails_helper'

describe '/api/login' do
  context 'negative tests' do
    context 'missing params' do
      context 'password' do
      end
      context 'email' do
      end
    end
    context 'invalid params' do
      context 'incorrect password' do
      end
      context 'with a non-existent login' do
      end
    end
  end
  context 'positive tests' do
    context 'valid params' do
    end
  end
end

Code source

Tips: You may receive an HTTP return status code 400 in case any parameter is missing along with an error message dictating, “email is missing/password is missing.”

Step 2: To move forward with our project, we will create a valid user by setting an email and password as original parameters. Next, we will customize this parameter for every particular spec by overriding it or omitting the email/password.

Here’s the code for the same.


describe '/api/login' do
  let(:email) { user.email }
  let(:password) { user.password }
  let!(:user) { create :user }
  let(:original_params) { { email: email, password: password } }
  let(:params) { original_params }
  ...

Code source

Step 3: We will extend our missing parameters as follows.


let(:params) { original_params.except(:password) }
it_behaves_like '400'
it_behaves_like 'json result'
it_behaves_like 'contains error msg', 'password is missing'

Code source

Note: We can make use of the same shared examples as expectations instead of repeating the expectations across the password and email contexts by uncommenting this given line in our rails_helper.rb file.


Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

Code source

Step 4: Next, we will have to add the 3 RSpec shared examples in spec/support/shared.rb as follows.


RSpec.shared_examples 'json result' do
  specify 'returns JSON' do
    api_call params
    expect { JSON.parse(response.body) }.not_to raise_error
  end
end

RSpec.shared_examples '400' do
  specify 'returns 400' do
    api_call params
    expect(response.status).to eq(400)
  end
end

RSpec.shared_examples 'contains error msg' do |msg|
  specify "error msg is #{msg}" do
    api_call params
    json = JSON.parse(response.body)
    expect(json['error_msg']).to eq(msg)
  end
end

Code source

Note: The example shared above is calling the api_cal method. It allows us to define the REST-like API endpoint only once in our spec.

Step 5: Define the api_call method as follows.


describe '/api/login' do
...
  def api_call *params
    post "/api/login", *params
  end
…


Code source

Step 6: Further, we will also have to customize the factory for our users. It can be done with in this way.


FactoryGirl.define do
  factory :user do
    password "Passw0rd"
    password_confirmation { |u| u.password }

    sequence(:email) { |n| "test#{n}@example.com" }
  end
end

Code source

Step 7: Run the migrations before we run our specs.


rake db:migrate

Code source

Other recommended ways to build a REST-like API in Ruby

3 recommended ways to build REST-like API in Ruby.webp

1. Roda

Routing tree web toolkit or Roda is a tiny framework that allows you to describe your routes as a tree in which each branch matches a part of the route. You can extend it using plugins that enable you to build your application from an easy to a complex level. Here's a code to understand the structure of Roda.


require "roda"

class App < Roda
  route do |r|
    # GET / request
    r.root do
      r.redirect "/hello"
    end

    # /hello branch
    r.on "hello" do
      # Set variable for all routes in /hello branch
      @greeting = 'Hello'

      # GET /hello/world request
      r.get "world" do
        "#{@greeting} world!"
      end

      # /hello request
      r.is do
        # GET /hello request
        r.get do
          "#{@greeting}!"
        end

        # POST /hello request
        r.post do
          puts "Someone said #{@greeting}!"
          r.redirect
        end
      end
    end
  end
end

run App.freeze.app

Code source

2. Sinatra

Sinatra is a level above Roda in terms of complexity. It has a slow pace and is more memory intensive but it is definitely simpler to work with as compared to Roda. Here’s an example to understand its structure that will show how it works exactly like Roda, but the major difference lies in the performance.


require 'sinatra'

get '/' do
  redirect '/hello'
end

before '/hello*' do
  @greeting = 'Hello'
end

get '/hello/world' do
  "#{@greeting} world!"
end

get '/hello' do
  "#{@greeting}!"
end

post '/hello' do
  puts "Someone said #{@greeting}!"
  redirect '/'
end

Code source

Tip: Sinatra is much suited for complex rest APIs and Roda is ideal for small and simple APIs.

3. Padrino

Padrino is built on top of Sinatra. It primarily aims to make Sinatra simpler and also add the convenience of Rails. This framework also leverages the convention over configuration approach that completes most of the grunt work in advance before a project is generated. You just have to structure your app into models, controllers, and views with no hassle to think about where to put each one of them.

Take a glance at its structure below for a brief understanding.


YourApp::App.controllers :hello do
  before do
    @greeting = 'Hello'
  end
  
  get :index do
    "#{@greeting}!"
  end
  
  get :world, map: '/hello/world' do
    "#{@greeting} world!"
  end
  
  post :index do
    puts "Someone said #{@greeting}!"
    redirect '/'
  end
end

Code source

Which one to choose?

With so many options available, it becomes tough to choose the tools for building rest-like APIs. However, by judging each on the important aspects like performance, ease of use, and community that is considered in a project, we can resolve this issue like a cakewalk. Here’s a sneak peek into the same.

1. Ease of use

This factor completely depends on the experience of the professional working with Rails. It comes as a safe choice if you have enough experience with it irrespective of its performance. However, if you are a beginner, Sinatra is one of the simplest choices whose learning curve is not steep and you can achieve a lot more with the very lesser code.

2. Community reach

The community strength associated with the particular framework indicates the ease of working with it. In Rails, the community is vast, which makes it easy to find gems and documentation. On the contrary, Roda and Padrino are not very well known in the industry. Since Padrino is built on top of Sinatra, many gems built for Rails will work with little or no adjustments in the same.

3. Performance

Roda surpasses all the other options in terms of performance. It is the fastest of all that are discussed above especially when it comes to the convenience of development and speed.

This was all about how you can build a REST API for Ruby from scratch. Since you have all the options on the table to build a REST-like API in Ruby, choose the one that boils down to your needs and knowledge. We hope you use it to your advantage to extend the functionality of your web application.

FAQs

1. What is a REST-based API?

Ans: Rest APIs or Restful API is an application programming interface that allows you to interact with restful web services and conform to the constraints of REST architectural style.

2. How does APIs work?

Ans: we can define An API as an abstraction of the web server. A website or mobile application makes an API call for data to be consumed by the end-user. API makes these requests and gets access to the web server to retrieve the data.

3. In what language are APIs written?

Ans: API is written in any modern programming language like Python, Ruby, Java, and JavaScript. Developers either have to install the packages or codes or most programming languages come with the ‌software to interact with the web APIs.

4. When should one build an API?

Ans: One should build an API when they wish to access the same data in different ways or places. When you wish to allow your partners or customers to limited or complete access to your data or to upsell your customers on direct API access.

Press

Press

What's up with Turing? Get the latest news about us here.
Blog

Blog

Know more about remote work.
Checkout our blog here.
Contact

Contact

Have any questions?
We'd love to hear from you.

Hire and manage remote developers

Tell us the skills you need and we'll find the best developer for you in days, not weeks.

Hire Developers