Go to Hackademy website

Générer la documentation d'une API REST (grape) à partir du code

Jonathan François

Posté par Jonathan François dans les catégories outils

Pour faire suite aux articles de Théo sur la mise en place d’une API RESTful avec l’utilisation de la gem Grape, nous allons aujourd’hui nous intéresser à la génération de la documentation de l’API.

Pour rappel, voici les liens ainsi que les dépôts Github de ces articles :

La rédaction de la documentation d’une API est une chose assez redoutée par de nombreux développeurs. Il est fastidieux de lister à la main tous les endpoints disponibles ainsi que tous les paramètres possibles associés avec leur explication. C’est la raison pour laquelle certaines personnes prennent des raccourcis, ce qui est dommage car une documentation bien conçu est la clé de la réussite de votre API (aussi bien publique que privée).

Même lorsqu’on nous prenons le temps de rédiger une documentation digne de ce nom, il faut ensuite la maintenir car une API a tendance à évoluer très vite et il faut que sa documentation reste à jour.

Afin de palier à ce problème, j’ai donc recherché un moyen de la générer automatiquement à partir du code. En effet cette solution permettrait de développer notre API tout en générant automatiquement sa documentation et donc sa mise à jour.

En réalisant mes recherches, je suis tombé sur des services sympa mais payant comme apiary.io et des services open source comme Docco, Dexy ou Swagger. Mon choix s’est rapidement arrêté sur Swagger.

En effet, Swagger permet de décrire, produire, consommer et visualiser les services d’une API RESTful (Démo). De nombreuses références comme Apigee, Microsoft ou Paypal l’utilisent et il est 100% open source.

Ici nous allons utiliser la gem Grape mais il est tout à fait possible de faire sans, pour cela vous pouvez jeter un oeil à ce dépôt Github.

Swagger et Grape

Demo_swagger

Swagger est développé en HTML et JavaScript et permet de créer une documentation interactive et très complète. Comme vous pouvez l’apercevoir sur le screenshot ci-dessus, il permet de catégoriser nos endpoints par ressource (pet, store ou user), puis utilise des codes couleurs pour spécifier le verbe HTTP utilisé sur l’endpoint. Au clic de l’un de ces endpoints vous avez le détail des paramètres attendus mais également le format de retour de l’API. Vous pouvez également consommer l’API depuis l’interface de documentation ce qui apporte une facilité de compréhension aux lecteurs.

Pour intégrer cette solution à notre projet Rails nous allons utiliser les gems suivantes :

Une fois notre Gemfile complété, passons à la configuration de ces gems.

Grape-swagger-rails

Créons un initializer qui contiendra la configuration de la gem grape-swagger-rails. Dans celui-ci nous allons renseigner le nom de notre documentation, l’url de notre application, ainsi que l’url souhaitée pour notre documentation.

GrapeSwaggerRails.options.url      = '/api/swagger_doc'
GrapeSwaggerRails.options.app_name = 'CarWorldTrader'
GrapeSwaggerRails.options.app_url  = 'http://localhost:3000'

Si vous utilisez différents environnements, je vous conseille de renseigner l’url de chacun dans votre fichier secrets.yml, afin de pouvoir renseigner l’option app_url de manière dynamique. Exemple :

GrapeSwaggerRails.options.app_url  = Rails.application.secrets.app_domain_name

Il faut ensuite ajouter les routes répondant à votre documentation :

# config/routes.rb
mount GrapeSwaggerRails::Engine => '/apidoc'

A ce stade, l’interface de documentation de votre API est disponible à cette url http://localhost:3000/apidoc mais vide.

Nous allons maintenant voir comment générer automatiquement notre documentation en modifiant légèrement notre code existant de l’API.

Grape-swagger

Commençons par mettre en place la configuration de la gem Grape_swagger pour générer notre documentation. Cela se réalise via l’utilisation de la fonction add_swagger_documentation.

# app/api/car_world_trader/base.rb
module CarWorldTrader
  class Base < Grape::API
    format :json
    prefix :api

    mount CarWorldTrader::V1::Cars
    add_swagger_documentation(
      base_path: "",
      api_version: "1.0",
      format: :json,
      hide_documentation_path: true,
      info: {
        title: "CarWorldTrader API",
        description: 'API to expose Cars informations form AutoTrader',
        contact: "jfrancois@synbioz.com",
        license: "All Rights Reserved"
        }
    )
  end
end
  • base_path : Chemin de base de l’API qui est exposée. Dans notre fichier routes.rb nous avions mentionnés mount CarWorldTrader::Base => '/'.
  • api_version : Version courante de l’API
  • format : Format de réponse de la documentation de l’API
  • hide_documentation_path : Permet de cacher les routes permettant à swagger d’afficher la documentation.
  • info : Informations relatives à l’API qui seront affichées sur votre documentation

D’autres options sont disponibles, je vous laisse consulter la documentation pour plus d’informations : Configuration.

A ce stade nous pouvons déjà voir la documentation générée. Nous allons utiliser la version 1.0 de l’API avec deux modifications :

  • suppression de l’authentification http basique
  • suppression du numéro de version dans l’url des endpoints

Nous n’avons pas besoin d’authentification http_basic pour nos tests, mais cela reste fonctionnel en l’utilisant. Il faudra juste renseigner les identifiants. Concernant ce point vous pouvez tout à fait mettre une authentification sur l’accès à la page de documentation de l’API. Pour cela, il suffit de le spécifier dans la configuration de la gem grape-swagger-rails, retrouvez les indications ici.

L’interface de documentation nous permet déjà d’obtenir la liste de toutes nos routes disponibles sur notre API avec la possibilité de la tester.

page_demo_1

L’interface répondant à la route /api/cars en POST :

page_demo_2

Correspondant au code suivant :

desc "Create a car"
params do
  requires :car, type: Hash do
    requires :manufacturer, type: String, regexp: /^[A-Z][a-z]+$/
    requires :design, type: String, values: ["tourer", "racing"]
    requires :style, type: String
    optional :doors, type: Integer, default: 3
  end
end
post do
  Car.create!(params[:car])
end

La description du code est donc utilisé par swagger pour documenter l’API. page_demo_3

Par défault la gem grape-swagger va se servir des définitions des paramètres de votre route pour générer la documentation. Il prend en compte les paramètres requis ou non, le type, la valeur par défaut, les valeurs possibles etc… En résumé tout ce que permet de définir la gem Grape sur les paramètres.

On peut donc générer la description ainsi que les informations sur les paramètres de notre endpoint. Afin de pouvoir documenter la réponse de l’appel API nous allons utiliser la gem grape-entity.

Cette gem permet de choisir et de définir les attributs que votre endpoint API va exposer. Par ce biais, la gem grape-swagger sera capable de l’inclure automatiquement dans notre documentation.

module CarWorldTrader
  module V1
    module Entities
      class Car < Grape::Entity
        expose :id, documentation: { type: 'integer', desc: 'Car ID' }
        expose :manufacturer, documentation: { type: 'string', desc: 'Car manufacturer' }
        expose :style, documentation: { type: 'string', desc: 'Car style' }
        expose :doors, documentation: { type: 'integer', desc: 'Car number of doors' } }
      end
    end

    class Cars < Grape::API
      # version 'v1', using: :path

      resource :cars do
        ...
        desc "Create a car", entity: CarWorldTrader::V1::Entities::Car
        params do
          requires :car, type: Hash do
            requires :manufacturer, type: String, regexp: /^[A-Z][a-z]+$/
            requires :design, type: String, values: ["tourer", "racing"]
            requires :style, type: String
            optional :doors, type: Integer, default: 3
          end
        end
        post do
          present Car.create!(params[:car]), with: CarWorldTrader::V1::Entities::Car
        end
        ...
      end
    end
  end
end

Le module Entities permet la création des différentes class dont nous allons avoir besoin (ici nous n’avons qu’une seule ressource Car). Nous spécifions ensuite les différents attributs que nous voulons exposer sur cette ressource via l’API. Retrouvez l’ensemble des paramètres possible pour la documentation sur le dépôt de la gem.

Ensuite nous allons intégrer cette documentation dans la description de notre route :

desc "Create a car", entity: CarWorldTrader::V1::Entities::Car

Ce qui va permettre de générer la mise à jour de l’interface de documentation en présentant le format et les attributs que va nous rendre l’appel API :

page_demo_4

Il faut maintenant dire à notre API d’utiliser cette classe Entities pour spécifier les attributs à exposer :

present Car.create!(params[:car]), with: CarWorldTrader::V1::Entities::Car

Dans notre cas, nous ne présentons pas les attributs created_at et update_at(juste pour l’exemple).

Conclusion

Avoir une interface de documentation qui s’adapte à notre code et se mettant à jour automatiquement est vraiment un gain de temps, d’autant que celle-ci permet de consommer l’API. Notre exemple est volontairement basique et nous n’avons pas parcouru l’ensemble des options possibles de la documentation mais le but de l’article est de vous mettre la puce à l’oreille sur ce genre d’outils.

En espérant que cela vous fasse gagner du temps et vous permette d’éditer des documentations API sans trop d’effort.

Les sources de cet article sont disponibles sur Github.

L’équipe Synbioz.

Libres d’être ensemble.

Articles connexes

Un plugin Vim à la mimine

03/01/2019

Dans l’article précédent, intitulé une assez bonne intimité, je vous présentais GPG et le chiffrement de courriels. Nous avons alors remarqué que le contenu d’un courriel était encodé de sorte que le...

Une assez bonne intimité

20/12/2018

Si vous êtes utilisateur de MacOS, il y a de fortes chances que vous utilisiez Apple Mail pour échanger des courriels. Et comme vous êtes sensible à la confidentialité des informations que vous...

Tests end to end avec Jest et Puppeteer

05/07/2018

Dans cet article, on va partir sur des tests end to end. Pour planter le décor, un test end to end (e2e) est un test logiciel qui a pour but de valider que le système testé répond correctement à un...

Chasser les requêtes N+1 avec Bullet

05/04/2018

Aujourd’hui nous allons parler des requêtes N+1 dans une application Rails : vous savez ces lignes quasiment identiques, qui s’ajoutent de manière exponentielle aux logs, dès lors que l’on appelle...