Skip to content

API Versioning, using a Bridge version

Eduardo J. edited this page Jan 13, 2026 · 3 revisions

Problem Statement & Solution Document for "API Versioning, using a Bridge version"

Reality?

Currently, the API lacks a versioning schema. Deploying any breaking changes to the existing "legacy" endpoints would immediately disrupt service for all current users, leading to integration failures and loss of trust.

Consequences?

Without a structured versioning path, we are trapped in a "frozen" state where we cannot improve the API without breaking the current user experience.

Future?

Our API should evolve to support new business requirements and improved data structures while providing a seamless experience for existing integrators.

Proposal!

We will introduce a non-obtrusive versioning layer. This involves:

  • Mapping all current legacy endpoints to a new /api/v1/ prefix to establish a formal versioning standard without altering existing logic.
  • Marking legacy routes as deprecated via HTTP headers to nudge users toward the /api/v1/ path.
  • Introducing an /api/v2beta/ namespace to test breaking changes and new features with early adopters before they are finalized.

Notes

One possible implementation could be:

# config/routes.rb
Rails.application.routes.draw do
  # 1. New Versioned Routes
  namespace :api do
    namespace :v1 do
      resources :users
    end
 
    # 2. Beta Routes (Pointing to V2 module early)
    scope 'v2beta', module: 'v2' do
      resources :users
    end
  end

  # 3. Legacy Routes (Point to V1 to avoid duplication)
  # This ensures existing clients don't break.
  scope module: 'api/v1', defaults: { legacy_route: true } do
    resources :users, only: [:index, :show]
  end
end

# app/controllers/api/v1/base_controller.rb
module Api
  module V1
    class BaseController < ActionController::API
      before_action :check_for_legacy_usage

      private

      def check_for_legacy_usage
        # Identify if the request reached the controller via a legacy route
        return unless params[:legacy_route]

        # Standard RFC headers for deprecation
        # Warning: 299 is the code for miscellaneous persistent warnings
        response.headers['Warning'] = '299 - "This endpoint is legacy. Please migrate to /api/v1/." '
        response.headers['Deprecation'] = 'true'
     
        # Link header provides the client with the correct alternative URL
        new_path = "/api/v1#{request.path}"
        response.headers['Link'] = "<#{request.base_url}#{new_path}>; rel='alternate'"
      end
    end
  end
end

Clone this wiki locally