REST API to GraphQL migration strategy - routing configuration (Ruby on Rails)

REST API to GraphQL migration strategy - routing configuration (Ruby on Rails)

In the previous post, I presented the preparatory work for migrating a REST API to GraphQL.

We saw how to improve the readability and robustness of your controller actions by refactoring and improving test coverage.

Here we will see how to properly configure the Ruby on Rails router to make both versions of our API available in order to ensure continuity of service for applications that consume our REST API.

Preface

This step is not necessarily required; you can simply add the default main GraphQL entry point, namely POST /graphql, to the Ruby on Rails routes.rb file.

However, on the project I worked on it seemed more relevant to show a real distinction between the two API versions.

Explanation

To filter and route our requests, you can use several methods.

Each method may be valid for your use case; however, URL-based versioning is not ideal.

Indeed, it introduces additional complexity to your API calls that isn't necessarily welcome, by adding a parameter that ultimately has little to do with the requested resources.

That said, I present the different methods to version your API:

  • You can, for example, version your API by the path in the URL
https://my-api.com/v1/articles

https://my-api.com/v2/articles
  • Or by a GET parameter
https://my-api.com/articles?version=1

https://my-api.com/articles?version=2
  • By a custom HTTP header
X-API-VERSION=1

X-API-VERSION=2
  • And finally by the HTTP Accept header

In the following example and for the rest of this post, we will assume we will use this last method.

To be valid, the value of this header must be in a MIME TYPE format.

ACCCEPT=application/vnd.le-nom-de-votre-organisation.vXXX

You can use any name you want between the ‘vnd' and the version.

You can replace XXX with the value of your API version.

Example

## api/contraints/version.rb
module API
  module Constraints
    class Version
      attr_reader :version
      attr_reader :default

   def accept_header_vendor
      "application/vnd.le-nom-de-votre-organisation.v#{@version}"
    end
  
      def initialize(options)
        @version = options.fetch(:version)
        @default = options.fetch(:default, false)
      end

      def matches?(request)
        @default || request
          .headers
          .fetch(:accept, "")
          .include?(accept_header_vendor)
      end
    end
  end
end

## app/routes.rb
Rails.application.routes.draw do
  scope module: :api, as: "api" do
    scope module: :v1, as: "v1", constraints: API::Constraints::Version.new(version: 1, default: true) do
      get "/posts", to: "posts#index"
      ...
    end
    
    scope module: :v2, as: "v2", constraints: API::Constraints::Version.new(version: 2) do
      post "graphql", to: "graphql#execute"
    end
  end
end

Using the default parameter of the API::Constraints::Version class allows automatic routing to the first version of your API if the Accept header is not present.

Conclusion

It is also possible to use a gem to address this issue, but I think it's not necessary to introduce an additional dependency for use cases covered by the examples provided in this post.

Finally, as stated above, in the context of migrating from REST to GraphQL, this step remains optional but it helps clearly delineate the evolution of your API.

Tags

  • graphql

  • rest

  • ruby

  • api

This article was posted on

Comments

Loading...

REST API to GraphQL migration strategy - routing configuration (Ruby on Rails) | DEMILY Clément