dnlgrv

Versioning your HTTP API - Ruby on Rails example

If you’re interested in why this example is here, this is the post it originated from: Versioning your HTTP API


You probably know this has to go in your config/routes.rb file somewhere, but where exactly? What you’re going to need to do is scope your API routes with a Constraint.

If your constraint’s matches? method returns true, then that scope’s routes will be applied to the incoming request. Here’s a class that you can put in lib/api_constraints.rb which will match on the application/vnd.my_app.v1 header:

class APIConstraints
  def initialize(options)
    @version = options[:version]
    @default = options[:default]
  end

  def matches?(req)
    req.headers["Accept"].include?(media_type) || @default
  end

  private

  def media_type
    "application/vnd.my_app.v#{@version}"
  end
end

In your routes file, you make use of it like so:

require "api_constraints" # you need to import the class from lib

Rails.application.routes.draw do
  scope module: :v1, constraints: APIConstraints.new(version: 1) do
    resources :users
  end

  scope module: :v2, constraints: APIConstraints.new(version: 2, default: true) do
    resources :users
    resources :comments
  end
end

Here we have two versions of our API (v1 and v2). v2 is currently the default API version that will be used.

The module declaration on the scope is also worth mentioning. It allows you to put your version-specific code in modules of the same name. For example, this is how I’d define the controllers:

class V1::UsersController < ApplicationController
end

class V2::UsersController < ApplicationController
end

And that’s it! Very easy to manage through the routes file. You can add new versions and change the default with ease.