Abstract

API versioning is needed in terms of development. It is often case that the only part of the API changes and remaining part stays the same in the next version. Copy and paste works of course but it is needed to update multiple versions when fixing bugs. Though branching is better than copy and paste, it is tedious and error prone. In this post I explain the way to share code between versions using rails engine.

Methods

Given API v1 provides basic CRUD of user resources.

Api::V1::Engine.routes.draw do
  resources :users
end
module Api
  module V1
    class UsersController < ApplicationController
      def index
        @users = User.all
      end

      def show ...
      def create ...
      def update ...
      def destroy ...
    end
  end
end
json.array! @users, partial: 'api/v1/users/user', as: :user

As you can see, v1 index API does not provide paging.

$ curl localhost:3000/api/v1/users.json
{"id":1,"name":"0"},
{"id":2,"name":"1"},
...

Now you decided to provide paging for v2 index API (which is incompatible with v1) but the remaining API (show, create, update and destroy) stays the same. In this case you can take advantage of rails engine and routes.

Api::V2::Engine.routes.draw do
  # Define index action only.
  resources :users, only: [:index]

  mount Api::V1::Engine, at: '/'
end

The key point is mounting the previous version at the bottom. That way when the request matches the current version routes, it hides the previous version, otherwise it automatically falls back to the previous version.

module Api
  module V2
    class UsersController < ApplicationController
      def index
        @users = User.page(params[:page])
      end
    end
  end
end
json.users @users, partial: 'api/v1/users/user', as: :user
json.metadata do
  json.page do
    json.next users_path(page: @users.next_page)
  end
end

As you can see below, v2 index provides paging information. At the same time show API (which is internally routed to v1) is also available under /v2 endpoint.

$ curl localhost:3000/api/v2/users.json
{
  "users":[
    {"id":1,"name":"0"},
    {"id":2,"name":"1"},
    ...
  ],
  "metadata":{
    "page":{
      "next":"/api/v2/users?page=2"
    }
  }
}

$ curl localhost:3000/api/v2/users/1.json
{"id":1,"name":"0"}

Conclusion

You can share most of the code between versions using rails engine. There are two key points.

  1. Define routes only you want to override.
  2. Mount the previous version at the bottom.

The working example is available at github. Thanks for reading, happy hacking!

References