Tagged with

Today we will create a Rails API. There are several ways to build an API in Rails.

If you're new to Ruby and Rails, check out the website GoRails - it will help you get started with a development environment for Rails. For this episode, we assume you already have this up and running.

Project

Formulae is a Rails based form management system. It comes in a few parts, the first being a Rails app that lets you create and manage complex forms. The second piece is a gem that you can plug into your Rails projects that acts as a client, allowing you to interact with the API’s resources. The third and final part is the front end stack, made up of a React app that renders the forms and provides the end user experience.

Today we will look at the creation of the first part, the Rails application that hosts and manages the forms. Let's get started by creating our Rails app:

rails new formulae

This will generate an entire Rails app for you. If you want, you can take a look in the folders and files it has generated.

We will use a linter in our project. It's called rubocop. Linters help us enforce coding standards. With Rubocop, we can decide which rules we will use. For example, here is our rules file that specifies our coding conventions. It will run before our tests in our Continuous Integration server, so if someone does not follow our coding standards, the build will fail. This is an interesting way to enforce coding style across your project, especially for open source projects when many people are used to writing in their own disparate styles.

Another interesting library that will run in our CI will be Brakeman, Brakeman helps us to find security vulnerabilities, especially if a library is outdated and has security issues. It’s a great thing to add to our build when we’re first getting started.

Setting up our tests

We will be using rspec to test our models. Additionally, we will use some libraries to help us when writing our tests. Rails by default uses minitest,which is a great testing library, but we prefer rspec so we’ll roll with it. The first thing we can do is remove the /test folder, since it is used by minitest and not rspec. Next, Let's add rspec-rails to our Gemfile and bundle

Now we can set up our Rails app for testing with rspec, using the following command:

rails generate rspec:install

It will generate some files and it creates a /spec folder where our files will be located. To run our tests, we just need to run rspec.

We will be using some other libraries to help us: Factory Girl to build models for our tests. Faker to produce realistic looking fake data in our factories. Shoulda Matchers to make our tests read a bit more nicely.

We’ll talk about these in more detail as we go. For now, let’s go ahead and add them to the Gemfile and bundle again to install them. Creating our Models

Our application’s data model contains Forms, Questions, Choices, and more. Let's create a Question model using Rails’ model generator.

rails g model Question key:string label:string content:text order:integer hidden:boolean

This generates our Question model, as well as the test files using rspec, and a migration.

The relations and validations are inside the model file, but if we want to check the fields, we need to check the migration.

Creating our Controllers

Since it is an API that needs to be versioned, we will not use a generator for controller. Instead, we will namespace the controllers under the app/controllers/api/v1 folder. We will use this standard to keep track of the files from our API, and provide an API version;in this case V1. Versioning our API means we can make changes in the future to it without worrying about breaking existing integrations.

We need to create our routes on routes.rb, in our routes we are using our namespace api and v1, corresponding to our version. When we

Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :questions
    end
  end
end

Now we have our routes for this controller, let's create a QuestionsController at this structure in our controllers folder: /api/v1/questions_controller.rb

It will have: index, show, update, and destroy methods. All these methods will return JSON for us.

Before we move on, we’ll create an ApiController that all of our API controllers inherit from. This way we can share common code between them.

Let's see how our index method would be:

  def index
    @questions = Question.all
    render json: @questions
  end

Or more simply:

  def index
    render json: Question.all
  end

In this case, we are using the method render to render the json for us.

The show method will get the question id and it will return the question object for us.

def show
    @question = Question.find(params[:id])
    render json: @question
end

As the Question.find will be re-used in other methods, we can create a before_action in our controller and say a method will be executed before specific actions. In our case:

before_action :find_question, only: %i[show destroy update]

Rails filters parameters by default to help protect against malicious requests. For the parameters to get passed through, we need to explicitly whitelist them. In our case we will have a method question_params that will do it for us.

def question_params
    params
      .require(:question)
      .permit(
        :key,
        :label,
        :content,
        :order,
        :hidden
      )
  end

Basically this method checks if the key is question and it checks the fields of this hash. So, we are validating we are really using the necessary fields for the Question.

We’ll use this in the update method. Let’s go ahead and add that. We’ll try to update, and we’ll tell the client whether or not it was successful.

  def update
    if @question.update_attributes(question_params)
      render json: @question, status: :ok
    else
      render json: @question.errors, status: :unprocessable_entity
    end
  end

And finally we'll add the destroy method, for removing a resource.

  def destroy
    if @question.destroy
      render json: :no_content, status: :no_content
    else
      render json: @question.errors, status: :unprocessable_entity
    end
  end

Here you can see what our finished controller looks like.

# frozen_string_literal: true

class Api::V1::QuestionsController < Api::V1::ApiController
  before_action :find_question, only: %i[show destroy update]

  def index
    @questions = Question.all
    render json: @questions
  end

  def show
    render json: @question
  end

  def update
    if @question.update_attributes(question_params)
      render json: @question, status: :ok
    else
      render json: @question.errors, status: :unprocessable_entity
    end
  end

  def destroy
    if @question.destroy
      render json: :no_content, status: :no_content
    else
      render json: @question.errors, status: :unprocessable_entity
    end
  end

  private

  def find_question
    @question = Question.find(params[:id])
  end

  def question_params
    params.require(:question).permit(:key,
                                     :label,
                                     :content,
                                     :order,
                                     :hidden,
                                     :question_type,
                                     :validate_as,
                                     :section_id)
  end
end

We have created other models to finish our application and you can see them in our repo at the tag add-models.

Summary

Today we saw how to create a Rails app, how to set up an API, and created some models and controllers. We also saw how to create our tests and some interesting libraries we can use in our app to make it better.

See you soon!

Resources


33b32be65cd662922a71dc1d051ddb9f?s=184&d=mm

Franzé Jr Software Engineer with experience working in multi-cultural teams, Franze wants to help people when he can, and he is passionate about programming and Computer Science. Founder of RemoteMeetup.com where he can meet people all over the World. When Franze is not coding, he is studying something about programming.