A complete guide for building microservices with Ruby on Rails – Part II

Assumption: You are referring to this sample API application along these series of posts.

In last post we covered introduction about Restful API’s and difference between the normal rails and API only rails application.

In this post we will see

  • What and why of API versioning?
  • Which strategy should be followed while versioning API’s?
  • Versioning of API’s using versionist gem.
  • Versioned API responses using active_model_serializers.

What and why of API versioning?

API versioning means keeping backward compatibility without breaking existing api while adding new features, to handle situations like following, in first place you should write versioned API’s.

Let’s say you have an API to sign-in users with username and password into the system and this API is being used by multiple clients like web, mobile etc.

curl -X POST \
 http://localhost:3000/users/login \
 -H 'Accept: application/vnd.mycompany.com; version=v1' \
 -H 'Content-Type: application/json' \
 -d '{"username": "pramod", "password": "passwd" }'

Now as a new requirement you want to allow users to sign-in using mobile number and otp.

What you’ll do? If you start updating exiting API it will break for existing API clients, in such scenarios its better to release new sign-in API with version v2 keeping backward compatibility and without breaking anything.

curl -X POST \
 http://localhost:3000/users/login \
 -H 'Accept: application/vnd.mycompany.com; version=v2' \
 -H 'Content-Type: application/json' \
 -d '{"number": "1234", "otp": "1234" }'

Various strategies of API versioning

There are various strategies for API versioning, here strategy meaning where to specify API version while requesting an API like in headers or in url path or as a url parameter.

Request Parameter

This strategy uses a request parameter to request a specific version of your API.

curl -X POST  http://localhost:3000/users/login?version=v1

Path

This strategy uses a URL path prefix to request a specific version of your API.

curl -X POST  http://localhost:3000/v1/users/login

HTTP Header

This strategy uses an HTTP header to request a specific version of your API.

curl -X POST http://localhost:3000/users/login \
     -H 'Accept: application/vnd.mycompany.com; version=v2'

Which strategy you should be following?

Out of three mentioned strategies which you should be following? answer is header strategy if you ask me why? then you didn’t understood restfulness other two strategies breaks the restful principle of URL must identify/locate the resource it should specify the version of resource, headers is the place where you should specify the resource version.

Adding first API version as v1

As in previous post we have seen how to generate api-only rails application, now let’s add first API version.

Creating V1 namespace and version v1

$rails g versionist:new_api_version v1 V1 --header=name:Accept value:"application/vnd.expense-manager.com; version=1"
Key things about versionist:new_api_version command
  1. Generates V1 controller namespace for api version v1 
  2. Using header strategy with name: Accept and value:”application/vnd.expense-manager.com; version=1″
  3. Adds following route block
api_version(
  module: "V2",
  header: {
    name: "Accept",
    value: "application/vnd.expense-manager.com; version=2"
  }
) do
  #API v1 routes goes here...
end

Creating v1 users controller

#Adding app/controllers/v1/users_controller.rb
$rails g versionist:new_controller users V1

At this point our V1::UsersController will look like

#app/controllers/v1/users_controller.rb
class V1::UsersController < V1::BaseController
  def index
    render json: User.all, each_serializer: V1::UserSerializer
  end
end

If you have noticed we are using Active model serializers for versioned responses. Explaining Active model serializers will need a whole new post, I would like to leave it as an exercise. V1::UserSerializer for version v1 response is as follows

#app/serializers/v1/user_serializer.rb
class V1::UserSerializer < ActiveModel::Serializer
  attributes :id, :email

  has_many :expenses
end

With above change we will end-up having following users routes for version v1

Screen Shot 2018-08-01 at 5.07.58 PM

Adding version v2 for API

We can add version v2 for users api’s using versionist generator commands and version v2 routes block, very similar to that of version v1. If you have followed everything correctly and add V2::UsersController and V2::UserSerializer making changes that are required in v2 then your controller, erializer  and routes should look something like following.

V2::UsersController:

#app/controllers/v2/users_controller.rb
class V2::UsersController < V1::UsersController
  def index
    render json: User.all, each_serializer: V2::UserSerializer
  end
end

V2::UserSerializer: 

#app/serializers/v2/user_serializer.rb
class V2::UserSerializer < ActiveModel::Serializer
  attributes :id, :email, :total_expenses

  def total_expenses
    object.expenses.sum(:amount)
  end
end

Notice the change in response for v2.

#In config/routes.rb
api_version(
  module: "V2",
  header: {
    name: "Accept",
    value: "application/vnd.expense-manager.com; version=2"
  }
) do
  resources :users
end

api_version(
  module: "V1",
  header: {
    name: "Accept",
    value: "application/vnd.expense-manager.com; version=1"
  },
) do
  resources :users
end

Now, start rails server and let’s see things in action! You will get JSON response for v1/users and v2/users API.

curl -X GET http://localhost:3000/users \
     -H 'Accept: application/vnd.expense-manager.com; version=1'

curl -X GET http://localhost:3000/users \
     -H 'Accept: application/vnd.expense-manager.com; version=2'

Note: If you are running code from this sample API application just change following line in class V1::UsersController < V1::BaseController as we yet to see how to authenticate API’s.

skip_before_action :authenticate!, only: [:login]
to
skip_before_action :authenticate!

You might have missed!

V2::UsersController is getting inherited from V1::UsersController this is important for supporting backward compatibility, means if you have another API in V1::UsersController like /users/expenses as following

#app/controllers/v1/users_controller.rb
class V1::UsersController < V1::BaseController
  def index
    render json: User.all, each_serializer: V1::UserSerializer
  end

 def expenses
  render json: @current_user.expenses, each_serializer: V1::ExpenseSerializer
 end
end

Then both the versions of /users/expenses will work, without adding it into V2::UsersController.


curl -X GET http://localhost:3000/users/expenses \
-H 'Accept: application/vnd.expense-manager.com; version=1'

curl -X GET http://localhost:3000/users/expenses \
-H 'Accept: application/vnd.expense-manager.com; version=2'

As we have seen how to add API versions keeping backward compatibility with the recommended strategy for versioning, We can conclude the part II of this series and see you soon with the next post on API Authentication.

If you have any queries please post in comments section and keep exploring!

Advertisements

A complete guide for building microservices with Ruby on Rails – Part I

As a Ruby on Rails developer I have built numerous API applications, I thought it will be a good idea to write a series of posts about building API application with Ruby on Rails.

Before you start the reading, please refer to this sample API application which has following technology stack.

Rails v5.2.0 
Ruby v2.4.0
MongoDB v3.4.2

In this series of posts I will cover following about API’s

  • Introduction to API’s, difference between normal and API only Rails application.
  • Versioning
  • Authentication
  • Throttling
  • Documentation and Testing

Brief about API’s

What are API’s?

API’s are nothing but Application Program Interface with set of routines, protocols and tools used for building software application, where consumer requests to the provider with uniform interface and receives the response in requested representation like JSON, HTML etc.

A high level diagram:

Screen Shot 2018-04-27 at 6.20.47 PM

But many times you might have heard that the API’s are Restful.

What are Restful API’s?

Restfulness is derived from Representational State Transfer design idiom for web services that embraces a stateless client-server architecture, having a resource that can be Created, Read, Updated or Deleted (CRUD operations). This resource with a URI(Uniform Resource Identifier) is transferred over a Uniform Interface like HTTP with representation (HTML or JSON or XML) whose state keeps on changing.    

Not understood?

Lets see how to build API’s with Rails

There are various options for building API’s, traditionally many developers were using gems like grape, rocket_pants or rails with name-spaced controllers and routes but building API’s with grape and rocket_pants gems is a tedious job as these gems have their own DSL.

If you choose rails with custom configurations for API’s, it will ease your job upto certain extent but rails is not built for API’s, As rails carries some unnecessary modules and middleware(s) alongside which are useless for api only application, then what to use? Well a better option which is developed for building api only applications is rails-api gem.

What is rails-api and How its different from Rails?

Rails-api – Rails for API only applications, in simple terms

  • Rails API = Rails – Middleware(s)
  • Without assets, views and helpers and much more…

You can create API only application after install latest stable rails version with following command

$rails new expense_manager --api
If you notice the option --api in above command, this is the option which is responsible for generating API only application with following two noticeable differences from traditional rails application.

1. A closer look at ApplicationController

In normal rails application, ApplicationController looks like

  class ApplicationController < ActionController::Base
    ...
  end
In rails api only application, ApplicationController inherits from ActionController::API
  class ApplicationController < ActionController::API
    ...
  end
Inheriting ApplicationController from ActionController::API has removed extra controller modules which are not necessary for API only applications, to check which all modules it has neglected compare the output of following commands.
  ActionController::API.ancestors - ActionController::Metal.ancestors
  ActionController::Base.ancestors - ActionController::Metal.ancestors

To name a few modules it has removed controller modules like ActionView::Layouts, ActionView::Rendering, ActionController::Helpers, ActionController::FormBuilder, ActionController::Flash, ActionController::Cookies etc.

2.  Handled set of middleware

In config/application.rb you can see the line

config.api_only = true

This line is responsible for removing middlewares that are not necessary for API only applications.

If you run

 $rails middleware 

in normal rails application you will see the following list of middlewares.

api_only_false

And in API only rails application middleware list looks like

api_only_true

From above lists of middlewares its clear that in API only application middlewares related to cache, cookies, session, flash etc. are removed making API application lighter and more suitable for API’s.

We have covered API introduction and key differences between normal and API only rails application. In the next post we will see how to structure and version your API’s, stay tuned.