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.
Comments
Loading...