Créer une API en Ruby on Rails avec la gem Grape

Publié le 9 décembre 2014 par Théo Delaune | back

Cet article est publié sous licence CC BY-NC-SA

Intéressons-nous aujourd’hui à la création d’une API minimaliste au sein d’une application Rails avec la gem grape. Cette gem est un micro-framework Ruby pour la conception d’une API REST. La version 0.9.0 est actuellement la version stable de Grape.

Nous parlerons surtout aujourd’hui de la mise en place d’un CRUD avec grape.

Nous verrons dans les prochains articles comment mettre en place un système un peu plus évolué qu’un simple CRUD ainsi que la gestion des erreurs.

Le CRUD représente 4 actions: create, read, update, delete. Ces termes désignent la création, la mise à jour, l’édition et la suppression d’un objet sur une base de donnée.

REST est une architecture d’application web. On peut la résumer à un liaison entre le client et le serveur sans état, c’est à dire que chaque requête doit contenir toutes les informations, le client doit donc garder au fur et à mesure les informations qui l’intéresse.

Notre application

Nous allons prendre le cas d’un concessionnaire automobile donnant accès via un web-service à son stock de voitures. Partons du principe que le côté web est réalisé par une autre personne, nous nous concentrerons juste sur la mise à disposition de ce web-service.

A la fin de cet article, le web-service donnera la possibilité de pouvoir gérer tout le stock de voitures via le CRUD mis en place.

Mise en place

Création de notre application de base Ruby on Rails

Nous allons pour cela créer notre application Ruby on Rails:

rails new AutoTrader

Une fois notre application créée nous allons pouvoir ajouter notre Modèle Car qui va représenter les voitures que nous allons gérer avec notre API.

cd AutoTrader/
bundle exec rails generate model Car manufacturer:string design:string style:string

# nous pouvons maintenant lancer les migrations pour persister ce modèle dans la base de donnée

bundle exec rake db:migrate

La base de notre application est réalisée nous allons pouvoir passer à l’étape la plus importante : la création de notre API.

Mise en place de grape

Nous allons ajouter la gem grape à notre Gemfile puis l’installer.

# Dans le fichier Gemfile
gem 'grape'
gem 'hashie_rails' # permet d'enlever la sécurité de strong_params, utile pour la gestion de ces validations avec Grape.
bundle install

La gem grape est maintenant installée sur notre application. Avant de poursuivre il nous faut préciser au moteur Rails l’endroit où notre API va être écrite.

Pour cet exemple j’ai choisi de créer l’API au sein du dossier app/api, pour ajouter ce chemin au moteur de Rails il nous faut modifier le fichier config/application.rb comme suit:

# Dans le fichier config/application.rb
config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]

La gem grape est maintenant correctement liée à l’application Rails, nous allons pouvoir rentrer dans le vif du sujet.

Architecture des fichiers de l’API

Nous allons mettre en place une architecture API spécifique à un numéro de version, dans le but de pouvoir par la suite créer plus facilement une “v2” par exemple.

Notre architecture se déclinera pour aujourd’hui avec deux fichiers:

  app/
    api/
      car_world_trader/
                base.rb
                v1/
                  cars.rb
  • base.rb est le fichier qui sera appelé par Rails pour le lancement de notre API. Il contient un appel vers le fichier v1/cars.rb
  • v1/cars.rb contient tout notre CRUD pour la gestion des voitures.

Il très important de garder les noms de dossiers et de fichiers qui correspondent aux noms de vos futurs modules et classes API, sans cela Rails ne pourra pas trouver votre API et lèvera une erreur.

Création de notre fichier base.rb

Ce fichier nous sert surtout à bien séparer notre API et permet aussi d’avoir un seul point de montage au sein de notre fichier config/routes.rb.

Si ce n’est pas déjà fait nous allons créer le répertoire car_world_trader/ et créer le fichier base.rb au sein de ce répertoire.

# Création du répertoire
mkdir -p app/api/car_world_trader

### Création du fichier base.rb
touch app/api/car_world_trader/base.rb

Nous allons maintenant pouvoir créer notre première Classe dépendante de grape.

# Dans app/api/car_world_trader/base.rb
module CarWorldTrader
  class Base < Grape::API
    mount CarWorldTrader::V1::Cars
  end
end
  • mount CarWorldTrader::V1::Cars va nous permettre de monter notre API grape que nous allons par la suite écrire dans le fichier v1/cars.rb.

Ajout de notre API dans les routes de notre application

Pour ce faire nous allons devoir ajouter cette route au sein du fichier config/routes.rb. Comme spécifié plus haut c’est notre fichier base.rb qui nous permet d’avoir un seul point d’entrée pour notre API dans les routes de l’application.

# Dans config/routes.rb
...
mount CarWorldTrader::Base => '/'
...
  • mount CarWorldTrader::Base => '/' est un point de montage pour notre API car grape utilise Rack pour pouvoir fonctionner. Nous allons lui affecter un point d’entrée sur ‘/’, nous verrons par la suite que nous pouvons préfixer les routes de notre API.

Mise en place de notre CRUD

Nous allons aujourd’hui prendre le cas d’un CRUD très basique, sans authentification, sans gestion des erreurs et au format JSON.

Commençons par créer le fichier au sein du répertoire v1:

# Dans le fichier app/api/car_world_trader/v1/cars.rb
module CarWorldTrader
  module V1
    class Cars < Grape::API
      version 'v1', using: :path
      format :json
      prefix :api
    end
  end
end
  • version 'v1', using: :path va représenter la version de notre API au sein de l’URL, exemple “/api/:version/”. Il existe plusieurs autre options, comme :header qui passe le numéro de version dans le header. En savoir plus sur le versioning Grape.

  • format :json indique que nous n’acceptons que du JSON en entrée de l’API.

  • prefix :api permet de préfixer l’API, car dans le fichier config/routes.rb l’api est accessible sur “/”. Maintenant grâce à cette option l’API est disponible sur “/api”

récupération de la liste des voitures et d’une seule voiture.

Nous allons encapsuler ces méthodes dans une resources :cars ce qui va permettre de rendre les voitures accessible sur l’adresse “/api/:version/cars”.

# Dans le fichier app/api/car_world_trader/v1/cars.rb
...
prefix :api

resource :cars do
  desc "Return list of cars"
  # Récupération de la collection de toutes les voitures grâce à ActiveRecord
  get do
    Car.all
  end

  desc "Return a car"
  # Récupération d'une voiture spécifique grâce au paramètre passé dans l'url
  params do
    requires :id, type: Integer, desc: "Car id"
  end
  route_param :id do
    get do
      Car.find(params[:id])
    end
  end
end
...
  • desc nous sert à décrire la méthode API que nous mettons en place dans le but de rendre notre API toujours lisible et compréhensible.

  • route_param :id permet de définir un namespace pour récupérer une voiture par son id.

Comme le format a été spécifié plus haut, chacune de nos deux méthodes va renvoyer les collections sous forme d’objet JSON.

Ajout d’une voiture via API

Nous allons maintenant permettre aux personnes utilisant notre web-service d’ajouter des voitures sur notre application.

# Dans le fichier app/api/car_world_trader/v1/cars.rb
# au sein de la resource :cars

desc "Create a car"
params do
  requires :car, type: Hash do
    requires :manufacturer, type: String
    requires :design, type: String
    requires :style, type: String
  end
end
post do
  Car.create!(params[:car])
end

Nous précisons à grape que cette méthode prend des paramètres en entrée. Au sein des paramètres nous spécifions avec un requires ceux obligatoires. Le paramètre car de type Hash doit comporter trois paramètres, “manufacturer”, “design”, “style”

Puis nous spécifions que cette méthode est accessible via un HTTP POST et nous créons avec ActiveRecord la voiture au sein de l’application en lui spécifiant le Hash.

Mise à jour d’une voiture

Nous pouvons maintenant ajouter la méthode permettant de mettre à jour une voiture via un HTTP PUT.

# Dans le fichier app/api/car_world_trader/v1/cars.rb
# au sein de la resource :cars

desc "Update a car"
params do
  requires :id, type: Integer, desc: "Status id"
  requires :car, type: Hash, desc: "Your updated car"
end
put ':id' do
  Car.find(params[:id]).update_attributes(params[:car])
end

Pour ce faire nous avons besoin de récupérer la voiture par cette URL “/api/:version/cars/:id”

Pour cela nous spécifions que notre méthode doit prendre deux paramètres en entrée : son id ainsi que l’objet de type Hash contenant la voiture. Comme nous voulons permettre de modifier librement la voiture il n’y a pas de paramètres requis au sein du Hash.

L’objet Voiture est ensuite mis à jour en utilisant ActiveRecord.

Suppression d’une voiture

Un utilisateur de l’API à aussi le droit de supprimer une voiture. Pour cela il va utiliser la méthode HTTP DELETE.

# Dans le fichier app/api/car_world_trader/v1/cars.rb
# au sein de la resource :cars

desc "Delete a car"
params do
  requires :id, type: Integer, desc: "Status id"
end
delete ':id' do
  Car.find(params[:id]).destroy
end

La suppression d’une voiture ressemble fortement à la sélection d’une voiture par son id. Le seul changement dans cette méthode est l’utilisation de la méthode HTTP DELETE et la suppression de l’objet sélectionné avec ActiveRecord.

Conclusion

Une fois l’application Ruby on Rails lancée, les routes de votre API sont:

GET        /api/:version/cars(.:format)
GET        /api/:version/cars/:id(.:format)
POST       /api/:version/cars(.:format)
PUT        /api/:version/cars/:id(.:format)
DELETE     /api/:version/cars/:id(.:format)

Nous avons vu aujourd’hui l’exposition d’une API très basique au sein d’une application Ruby on Rails grâce à la gem grape. Nous nous retrouverons dans de prochains articles pour améliorer notre API avec l’ajout de la gestion des erreurs, l’ajout d’une authentification basique puis de la création d’une version 2 de notre API.

Les sources de cet article sont disponibles sur GitHub.


L’équipe Synbioz. Libres d’être ensemble.