Blog

Ari
May 28, 2017
Ruby on Rails Tutorial No Comments

Rails tutorial: Using Rails as JSON API

In this Rails tutorial, we will learn to use Ruby On Rails as a JSON API. This feature was already made in Rails back in around 2012 but it was based on standalone Rails::API gem. Now, this gem has been integrated into Rails. The idea behind this is creating API for clients to access data and application logic. In other language, it is backend for other client side frameworks like Angular or Android application or desktop application and so on. A rails application can serve both way both rendering html and json data. But that is not what we are talking about. We are only interested in creating Rails as our JSON API backend.

Advantages of using Rails as JSON API

  • No need of view templates – HTML,CSS,JS
  • View layer gets separate from application logic
  • Less code – no turbolinks, jquery
  • We can use all Rails core features without any problems like bundler, ruby gems etc.
  • We get to work with same Rails framework and continue using features like test cases different test, development,

Before we begin learning Rails API we need be sure we have ruby >=2.2.2 and rails version 5. You can check with following command :

$ ruby -v

$ rails -v

API Endpoints of our app

Our app will expose the following endpoints to external apps. External apps will communicate by making the use of the following HTTP verbs to perform actions like create, update, delete and request resource. For this tutorial we consider we have two resources articles and comments. Each article is associated to have many comments. If you are new to HTTP verbs, GET fetches the data/resource, POST creates or adds the new resource, PUT updates the resource and DELETE destroys the resource.There are more HTTP verbs but not very common.

POST /signup

POST /auth/login

GET /auth/logout

GET /articles

POST /articles

GET /articles/:id

PUT /articles/:id

DELETE /articles/:id

GET /articles/:id/comments

PUT /articles/:id/comments

DELETE /articles/:id/comments

Project Setup

Lets create a project for our api app by running the command below.

$ rails new blog_api –api -T

This command generates the new Rails application configured to serve JSON backend. The –api flag tells rails we want API application. The -T flag tells rails not to generate the default Mintest files for testing. Rails application are generally tested using Rspec framework. You can read more about it here.

Models And Associations

Let’s start by generating the article model.

$ rails g model Article title:string description:string excerpt:string

So, we have title, description and excerpt as the parameters of article model. The above command must generate a migration file. Let’s run the migration file to create the database.

 

$ rails db:migrate

We also have the comment model. Let’s generate it.

$ rails g model Comment body:string article:references


Here, by adding article:references we are telling rails to add association with article model. This does two thing one is add foreign key to comments table and add belongs_to in comment.rb file. Now, let’s run the migration.

$ rails db:migrate

We have only one way association here. We should also add association from article to comment direction. Add has_many to article.rb file. Your article model file should look like the following.

class Article < ApplicationRecord
 # model association
 has_many :comments

 # validation
 validates_presence_of :title  validates_presence_of :description
end

In your comment.rb model file.

class Comment < ApplicationRecord
 # model association
 belongs_to :article

 # validation
 validates_presence_of :body
end

Remember, you can add the validations and callbacks as per required.

Controllers and rendering JSON

We have our model set up. Now we need to generate controllers for our model. Let’s generate the articles and comments controller.

$ rails g controller articles

 

$ rails g controller comments

So we have articles and comments controller. Fill these controllers with the code below and I will explain what’s going on.

In articlescontroller.rb file

# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
 before_action :set_article, only: [:show, :update, :destroy]

 # GET /articles
 def index
   @articles = Article.all
   json_response(@articles)
 end

 # POST /articles
 def create
   @article = Article.create!(article_params)
   json_response(@article, :created)
 end

 # GET /article/:id
 def show
   json_response(@article)
 end

 # PUT /article/:id
 def update
   @todo.update(article_params)
   head :no_content
 end

 # DELETE /article/:id
 def destroy
   @article.destroy
   head :no_content
 end

 private

 def article_params
   # whitelist params to prevent CSRF attacks
   params.permit(:title, :description, :excerpt)
 end

 def set_todo
   @article = Article.find(params[:id])
 end
end

So our articlescontroller is done. Now let’s work on commentscontroller. Add the following code.

In CommentsController.rb file.

# app/controllers/comments_controller.rb
class CommentsController < ApplicationController
 before_action :set_comment
 before_action :set_comment_article, only: [:show, :update, :destroy]

 # GET /article/:article_id/comments
 def index
   json_response(@article.comments)
 end

 # GET /articles/:articles_id/comments/:id
 def show
   json_response(@comment)
 end

 # POST /todos/:todo_id/items
 def create
   @article.comments.create!(comment_params)
   json_response(@article, :created)
 end

 # PUT /articles/:article_id/comment/:id
 def update
   @comment.update(comment_params)
   head :no_content
 end

 # DELETE /articles/:article_id/comment/:id
 def destroy
   @comment.destroy
   head :no_content
 end

 private

 def comment_params
   params.permit(:body)
 end

 def set_article
   @article = Article.find(params[:article_id])
 end

 def set_article_comment
   @comment = @article.comments.find_by!(id: params[:id]) if @article
 end
end

Authentication

Let’s generate a User model with some properties. Execute the following code.

$ rails g model User name:string email:string password_digest:string

Run the migration

$ rails db:migrate

Now we need a bcrypt gem as the dependency for encrypting the password in the database. Put the gem in the Gemfile.

..

gem ‘bcrypt’, ‘~> 3.1.7’

..

Run the bundler

$ bundle install


You need to make some changes in your User.rb file inside models, make sure they look simliar to the following.

# app/models/user.rb
class User < ApplicationRecord
 # encrypt password
 has_secure_password

 # Model associations
 has_many :articles, foreign_key: :created_by
 # Validations
 validates_presence_of :name, :email, :password_digest
end

Now the password is encrypted in the database. The next thing we need to do is add another column called token to the User table. Run the command below.

$ rails generate migration add_authentication_token_to_users auth_token:string


We need to make sure no two users have the same token. So let’s make some changes to migration file. You should change the migration as below.

def change
   add_column :users, :auth_token, :string, default: “”
   add_index :users, :auth_token, unique: true
 end


Also make a minor change to user model

class User < ActiveRecord::Base
 validates :auth_token, uniqueness: true
 .
 .
 .
end


Now we need a sessions controller. Create one

$ rails g controller sessions create destroy

The create action should look like the following in the sessions controller.

def create
   user_password = params[:session][:password]
   user_email = params[:session][:email]
   user = user_email.present? && User.find_by(email: user_email)

   if user.valid_password? user_password
     sign_in user, store: false
     user.generate_authentication_token!
     user.save
     render json: user, status: 200, location: [:api, user]
   else
     render json: { errors: “Invalid email or password” }, status: 422
   end
 end

 

Add the following code to destroy action

def destroy
   user = User.find_by(auth_token: params[:id])
   user.generate_authentication_token!
   user.save
   head 204
 end

You can read more about authentication from here.

Versioning the api

Versioning is very important because you can make the transition to one version of code to another without affecting the client’s code. So you can aware clients about the breaking changes and follow the version. The simple example of versioning is given below.

Rails.application.routes.draw do
 namespace :v1 do    resources :articles    resources :sessions, only: [:create,:destroy]  end
end

You can read more about versioning and how to do it from this link.

Pagination

Pagination is very important when you have lot of data. You don’t want to render all of the data from database upon a request don’t you? So to achieve this we need to use a gem called will_paginate. This will break the data into the number we specify and it will render the data only how much we specify. This is very handy and are used in index action of controllers generally. In your Gemfile add the following line

..

gem ‘will_paginate’, ‘~> 3.1.0’

..

Run the bundler to install the gem.

In the articlescontroller.rb file in index action change it like this

..

def index
   # get paginated current user todos
   @articles = Article.paginate(page: params[:page], per_page: 20)
   json_response(@articles)
 end

..

Now your controller must render only 20 data per request.

Conclusion

Building an API need a strong test coverage. You should use testing framework like Rspec and so on. Also tools like Postman are very handy while developing API. Although this is a quite long article it is still the basic.

Do you want to know more about testing? you might want to read Ruby on Rails Tutorial: Better Testing with FactoryGirl

Comment