Strategie de migration API REST vers GraphQL - configuration du routing ( Ruby on Rails )

Dans le précédent billet, je vous ai présenté la partie travail préparatoire en vue de la migration d’API REST vers GraphQL.
Nous avons vu comment améliorer la lisibilité et la robustesse de vos actions dans les controllers en faisant un refactor et en améliorant la couverture de test.
Nous allons ici voir comment bien configurer le router de Ruby on Rails pour rendre disponible les deux versions de notre API afin d'assurer une continuité de service pour les applications qui consomment notre API REST.
Avant propos
Cette étape n’est pas forcement nécessaire, vous pouvez simplement ajouter le point d’entrée principale de GraphQL par défaut, à savoir POST /graphql au fichier routes.rb de Ruby on Rails.
Toutefois, sur le projet sur lequel j’ai travaillé cela semblait plus pertinent de montrer une vraie distinction entre le deux versions d’API.
Explication
Pour filtrer et router nos requêtes, vous pouvez utiliser plusieurs méthodes.
Chaque méthodes peut être valable pour votre cas d’utilisation, néanmoins l’utilisation du versionning par URL n’est pas vraiment idéale.
En effet, il introduit de la complexité supplémentaire a vos appels API qui n’est pas forcement la bienvenue en ajoutant un paramètre qui n’a finalement pas grand chose à faire a côté des ressources solicité.
Ceci étant dit, je vous présente les différentes méthodes pour versionner votre API :
- Vous pouvez par exemple versionner votre API par le chemin de votre URL
https://my-api.com/v1/articles
https://my-api.com/v2/articles
- Ou encore par un paramètre GET
https://my-api.com/articles?version=1
https://my-api.com/articles?version=2
- Par un custom header HTTP
X-API-VERSION=1
X-API-VERSION=2
- Et enfin par header HTTP Accept
Dans l’exemple suivant et pour le reste du billet, nous allons considérer que nous utiliserons cette dernière méthode.
Pour être valide, la valeur de ce header doit être d'un format MIME TYPE.
ACCCEPT=application/vnd.le-nom-de-votre-organisation.vXXX
Vous pouvez utiliser le nom que vous voulez entre les ‘vnd’ et la version.
Vous pouvez remplacer XXX par la valeur de votre version d’API.
Exemple
## 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
L’utilisation du paramètre default
de la classe API::Constraints::Version
permet de router automatiquement sur le première version de votre API si le header accept n’est pas présent.
Conclusion
Il est également possible d’utiliser une gem pour adresser cette problématique, mais je pense qu’il n’est pas nécessaire d’introduire une dépendance supplémentaire pour des cas d’utilisations couverts par les exemples fournis dans ce billet.
Enfin, comme énoncé plus haut, dans le cadre de la migration REST vers GraphQL, cette étape reste optionnelle mais elle permet de bien démarquer l’évolution de votre API.