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.
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
/test folder, since it is used by
minitest and not
Let's add rspec-rails to our
Now we can set up our Rails app for testing with
rspec, using the following
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
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
at this structure in our controllers folder:
It will have:
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.
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
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
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!
- Ruby on Rails Website
- Ruby on Rails Guides
- Help you set up your Ruby on Rails environment
- Code for this episode + other models
- Render method
- 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.