Search code examples
ruby-on-railsapiruby-on-rails-5active-model-serializersrails-api

Correct way to implement API versioning with active_model_serializers


I know there are already some questions and also this is a open issue regarding AMS not handling namespaces too efficiently (which is used by this versioning approach) but I wanted to be sure I am in the right track within the current constraints.

Right now I am using Rails 5 and AMS 0.10.1, so I did the following:

# config/initializers/active_model_serializer.rb
ActiveModelSerializers.config.serializer_lookup_enabled = false

to disable default serializer lookup (which didn't work anyway); and

# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  def get_serializer(resource, options = {})
    unless options[:each_serializer] || options[:serializer] then
      serializer = (self.class.name.gsub("Controller","").singularize + "Serializer").constantize
      resource.respond_to?(:to_ary) ? options[:each_serializer] = serializer : options[:serializer] = serializer
    end
    super(resource, options)
  end
end

to override how serializers are found by default; my controllers and serializer are like this:

# app/controllers/api/v2/api_controller.rb
module Api::V2
  class ApiController < ApplicationController
    ...

# app/controllers/api/v2/users_controller.rb
module Api::V2
  class UsersController < ApiController
    ...

and

# app/serializers/api/v2/user_serializer.rb
module Api::V2
  class UserSerializer < ActiveModel::Serializer
    ...    

Now, things like ActiveModel::Serializer.serializer_for(object) won't work, so I had to also monkey patch my request specs using example.metadata[:api_version] to set the API version before each test and raising and error if the example didn't set it.

So:

  1. Is there a better way documented?
  2. Is this any close to being correct?
  3. Will I be facing problem further in with this approach?
  4. How can it be improved?

Solution

  • Since I haven't found a better way, neither documented nor anywhere, it also seems to be correct and I haven't faced problems after a while using it, this appear to be a good approach for API versioning.

    Anyway, I advise caution using this approach to not change behaviour of older supported versions for your API. Test carefully and notify your clients for deprecations and support removal for old versions.