đš Cette Ă©dition est relativement longue, votre boĂźte mail risque de la tronquer. Je vous conseille de cliquer sur le titre ci-dessus pour lâouvrir dans votre navigateur web. âïž
Au programme aujourdâhui :
L'API la plus parfaite par Thomas
Lâagence de recrutement pour les leads dĂ©vs RoR !
Temps de lecture : 13 minutes
Hello les petits Biscuits !
Bienvenue sur la 16Úme édition de Ruby Biscuit.
Vous ĂȘtes maintenant 386 abonnĂ©s đ„ł
Je vous prĂ©sente Ruby Biscuit sous son vrai visage et bien dĂ©corĂ© pour les fĂȘtes !
Voici 2 annonces qui pourraient vous intéresser :
Le Wagon Paris organise un salon du recrutement qui aura lieu la semaine prochaine, le vendredi 15 dĂ©cembre. Si vous ĂȘtes Ă la recherche de profils tech (dev junior, data et product) pour agrandir vos Ă©quipes vous pouvez vous y inscrire ici !
La communautĂ© de Rubyist Toulouse.rb reprend du service ! Si vous ĂȘtes de la rĂ©gion nâhĂ©sitez pas Ă aller leur faire coucou le 19 dĂ©cembre lors de leur prochain meetup
Avant de vous laisser entre les mains de Thomas pour lâarticle du mois, je tenais Ă vous rappeler que vous ĂȘtes tous les bienvenus pour donner votre avis en commentaire et partager vos expĂ©riences sur les sujets que nous abordons. Vous pouvez aussi mettre un petit like â€ïž et/ou partager la newsletter Ă un copain ou une copine ! đ
Bonne lecture.
L'API la plus parfaite
Bonjour, fier membre de la communauté Ruby Biscuit ! Il n'est pas rare dans notre profession d'avoir à s'interfacer avec des applications tierces que ce soit pour utiliser des services externes, pour permettre l'accÚs à nos données, ou tout simplement pour communiquer avec une appli front !
Alors c'est de ça qu'on va parler aujourd'hui : les API ! Et plus prĂ©cisĂ©ment, comment faire lâAPI la plus flexible possible cĂŽtĂ© client et la plus customisable possible (en terme de droits d'accĂšs), tout en gardant un code clair et maintenable. En tout cas c'est ce qu'on va essayer de faire aujourd'hui đ !
Json:api.org
Pour le cÎté "flexible" de notre API, on va s'appuyer sur la norme jsonapi.org. C'est une norme relativement populaire incluant beaucoup de mécaniques qui, si on les implémente, vont rendre notre API "top tier".
L'autre avantage de suivre cette norme, c'est qu'on va pouvoir s'appuyer sur des outils déjà créés par la communauté ! Alors il est temps d'installer des gems, ou plutÎt une gem : jsonapi.rb.
Cette gem va nous faciliter la vie comme c'est pas permis :
Elle nous ajoute la sérialisation dans le format jsonapi.org (via une gem dépendante, jsonapi-serializer)
La désérialisation (pratique quand on s'attaquera aux
create
etupdate
)Elle ajoute les inclusions
Les sparse fields
La recherche et l'ordonnancement (via une gem dépendante, ransack)
Et en plus de ça la pagination et mĂȘme le support des erreurs !
Franchement, cette gem (et ses dĂ©pendances), chapeau, merci, gracias, thank you, terima kasih, arigatĆ, y tutti quanti !
Installation
L'installation de la gem est simple, on l'ajoute au Gemfile et on écrit JSONAPI::Rails.install!
dans un initializer.
Pour le reste, on va créer trois modÚles histoire de pouvoir vous faire une démonstration. Dans l'idée on dit qu'on est un site marchand et qu'on vend des livres de plusieurs éditeurs.
rails g model Publisher name:string client_id:string
rails g model Author name:string description:text
rails g model Book publisher:references author:references name:string price:integer
Voici le fichier de seed :
Act As JSONAPI
Avant de se lancer à corps perdu dans le développement de nos controllers, on va structurer notre code et on va mettre toute la logique générique de notre API dans une DSL qu'on va appeler ActAsJSONAPI
(et oui, pensez Ă mettre API
et JSONAPI
dans vos inflections).
Alors voilà à quoi ça ressemble (Pssss, si tu es dans ton navigateur, tu veux agrandir les images avec les petites flÚches en haut à droite) :
Sur le hook d'inclusion :
Puis les, ou plutĂŽt âlaâ, mĂ©thode de singleton qui nous permet de dĂ©finir notre DSL :
Et maintenant les méthodes d'instance :
Bon, c'était déjà un gros morceau mais on a pas fini. D'abord je vous montre les modules custom qu'on include au tout début de la DSL.
On un module pour la gestion des erreurs :
Je ne vous tiens pas la main pour celui-lĂ , il s'occupe de la gestion et du formatage des erreurs, et si vous n'ĂȘtes pas en environnement de production, il vous rajoute mĂȘme la backtrace (parce que quand on rescue, on perd la backtrace dans les logs vu que le serveur ne fait plus une 500)
Et le suivant, c'est un module pour gérer les autorisations :
Pour l'instant, il nous sert juste à vérifier que notre controller effectue bien la mécanique d'autorisation sur les filtres permis. C'est pas grand chose pour le moment mais encore une fois, on l'étoffera quand on étoffera notre mécanique d'autorisations.
Et enfin, les actions par défaut qui sont ajoutées optionnellement en fonction de la configuration de la DSL :
On fait pareil pour le show :
Et pour le destroy :
Et on va s'arrĂȘter lĂ . On ne verra pas les actions de create et d'update tout de suite, il nous faudra mettre en place d'autres gros blocs de code avant ça alors faisons dĂ©jĂ fonctionner ce quâon a. Ăa serait bien !
MVC
Okay ! Maintenant qu'on a setup la base de notre super API, il est temps de voir ce que ça vaut. Alors on va trÚs rapidement créer les controllers et serializers qu'il nous faut.
Pour le controller, c'est assez simple, on déclare la constante, on y met notre DSL et le tour est joué :
Bon, je vous ai mis que le controller pour Book mais imaginez que je fais la mĂȘme chose pour les Publisher et Author (et si vous ne savez plus de quoi on parle, retournez au dĂ©but de l'article ^^)
De la mĂȘme façon on ajoute les serializers, mĂȘme topo je vous montre que celui des Book :
Et enfin faut ajouter les routes m'enfin vous ĂȘtes suffisament balĂšzes pour faire sans mon aide !
Démo
Et ça y est ! On y est enfin ! La démo ! On va pouvoir voir cette magie que je vous promets depuis le début. Alors, est-ce que ça valait vraiment toute cette peine ?
On va commencer petit avec
http://localhost:3000/api/v1/books
Whaowwwwww ... www ... Bon, en vrai j'ai plus à vous montrer mais déjà je veux vous familliariser avec la structure de la spec jsonapi.org.
Vous pouvez remarquer que les données "principales" sont dans une clé data
. A l'intérieur, pour chaque élément on a le type, l'id, sympa, puis les attributes et les relationships.
Les attributes c'est exactement ce qu'on imagine. Par contre les relationships sont plus intéressantes puisqu'elles ne contiennent, pour chaque association que vous avez mises dans votre serializer, uniquement le type et l'id de ou des objets associés. Gardez ça dans un coin de votre esprit on va y revenir. Et puis vous avez la clé links qui donne le lien vers ce record en particulier.
Outre la clé data
, on a une clé meta
(vous vous souvenez qu'on avait fait une méthode pour pouvoir personnaliser cette clé ?) et une clé links
plutÎt intéressante puisqu'elle contient les infos de navigation liés à la pagination !
Sauf que là , la pagination c'est rigolo mais on a 4 records alors que la taille par page est à 30 par défaut (ça vient de la gem). Pas d'inquiétude, on peut changer ça avec le params page, par exemple
http://localhost:3000/api/v1/books?page[size]=2&page[number]=2
Okay, là c'est déjà plus sympa. Un lien vers la page précédente et vers la premiÚre page. Et si on était pas sur la derniÚre page on aurait aussi un lien vers la page suivante et vers la derniÚre page.
Mais attendez, y'a pas que la pagination ! Je vous avais dit de mettre les relationships dans un coin de votre tĂȘte. Bon et bien si vous l'aviez fait tant mieux parce que tout de suite je vous montre les inclusions, avec ça
http://localhost:3000/api/v1/books?include=publisher
ARRĂTE ! ArrĂȘte ! Annule tout !
On a un problĂšme et pas des moindres, les plus perspicaces d'entre vous l'auront Ă©videmment vu mais on a une N+1 sur les bras (pour ceux qui ignorent ce qu'est une N+1, c'est un terme qui vient des mesures de complexitĂ© d'algorithmes et il faudrait demander Ă un scientifique de l'informatique plus de prĂ©cisions mais dans le contexte ça veut dire qu'on fait beaucoup plus de requĂȘtes SQL que ce qu'on devrait faire) !
Heureusement, le correctif n'est pas bien compliquĂ©, il suffit d'includes dans notre requĂȘte les mĂȘmes choses que ce qui est demandĂ© dans l'url.
On ajoute une petite méthode ici :
Une autre par lĂ :
Et on override les serializers pour lazy_load
dans le cas oĂč le include n'est PAS demandĂ© :
Maintenant, on peut repartir
http://localhost:3000/api/v1/books?include=publisher
C'est beaucoup mieux, et la réponse ?
Le premier changement comparĂ© Ă tout Ă lâheure, c'est dans les relationships, on voit que lâassociation author n'est pas chargĂ©e (par contre elle apparaĂźt pour que le client comprenne qu'elle existe).
Ensuite, on voit qu'à la racine en plus de la clé data
, il y a une clé included
qui contient des objets aussi. Ce sont tous les objets qu'on a inclus au travers des associations !
(Une brÚve mise en garde cependant, le included n'est pas paginé, ce qui rends particuliÚrement dangereux le fait d'exposer des relations has_many
dans votre API. Mon conseil c'est de n'exposer que les associations belongs_to
.)
Non mais n'empĂȘche ! Une API qui vous permet de rĂ©cupĂ©rer les associations en une seule requĂȘte ! Et sans N+1, mĂȘme en GraphQL ça aurait Ă©tĂ© galĂšre Ă faire ! Et c'est pas tout !
Les "sparse fields" ça vous parle ? Comme en GraphQL encore une fois c'est la capacité à sélectionner les champs que vous voulez dans votre retour, par exemple
http://localhost:3000/api/v1/books?fields[book]=name
Et hop, pas d'infos en trop, juste pile-poil ce qu'il vous faut ! Mais vous en voulez
plus ? La possibilitĂ© d'ordonner les rĂ©sultats peut-ĂȘtre ? C'est parti
http://localhost:3000/api/v1/books?sort=price
Pas totalement impressionnĂ© ? Et si on trie par prix dĂ©croissant, puis par nom par ordre alphabĂ©tique si c'est le mĂȘme prix ?
http://localhost:3000/api/v1/books?sort=-price,name
C'est pas mal hein ? Ho et je ne vous ai pas encore parlé de la recherche ! Dites ce que vous voulez et je vous le trouve ! Tous les livres avec un prix strictement inférieur à 12⏠? Tout de suite
http://localhost:3000/api/v1/books?filter[price_lt]=1200
Ou alors, vous faites un champ de recherche et vous voulez chercher aussi bien dans le titre des livres que dans le nom des éditeurs, ça marche aussi !
Avec "Gaston" comme valeur pour la recherche
http://localhost:3000/api/v1/books?filter[name_or_publisher_name_cont]=Gaston
Et la mĂȘme requĂȘte avec "Dupuis" comme valeur
http://localhost:3000/api/v1/books?filter[name_or_publisher_name_cont]=Dupuis
C'est pas incroyable ça ! Toute la recherche sur tous vos champs de base de donnée sans avoir aucun code custom à écrire. C'est plus une API c'est un ORM le truc !
Niveau supĂ©rieur đ
Pour finir avec la démo, j'ai envie de vous montrer deux exemples de ce qu'on peut faire en combinant les fonctionnalités de notre API.
Imaginons qu'on veuille afficher le prix du livre le plus cher pour un éditeur donné, on pourrait faire :
http://localhost:3000/api/v1/books?filter[publisher_client_id_eq]=dupuis&sort=-price&page[size]=1&fields[book]=price
Avec le sparse field on récupÚre uniquement le prix, puisque c'est ça qui nous intéresse. Avec la pagination on ne récupÚre qu'un seul élément et avec le tri on récupÚre celui avec le plus grand prix. Et avec les filtres de recherche, on ne prend que les records associés à l'éditeur "Dupuis".
Essayez de faire ça avec n'importe quel autre style d'API, ça risque d'ĂȘtre un peu plus galĂšre vous ne pensez pas ?
Autre exemple, on a une page avec un champ de recherche par auteur et quand on valide la recherche on veut afficher tous les Ă©diteurs chez qui cet auteur a publiĂ© un livre, avec la liste de tous les livres de cet Ă©diteur et le nom de l'auteur pour chaque livre. Ăa donne quelque chose comme
http://localhost:3000/api/v1/publishers?filter[books_author_name_cont]=Bes&include=books,books.author&fields[publisher]=name,books&fields[book]=name,author&fields[author]=name
Ici on utilise les filtres pour restreindre sur le nom de l'auteur en passant par la chaßne d'association books puis author. On inclut également ces deux associations et on utilise les sparse fields sur les trois types de donnée différents pour ne sélectionner que ce qu'on veut.
Alors oui, je sais que c'est bizarre d'afficher tous les livres de l'éditeur et pas juste ceux qui ont été publiés par l'auteur de la recherche. J'ai pris cet exemple pour vous montrer qu'on pouvait inclure des associations en chaßne mais si on voulait faire quelque chose de plus logique on aurait pu faire
http://localhost:3000/api/v1/books?filter[author_name_cont]=fran&include=publisher,author&fields[publisher]=name,books&fields[book]=name,author&fields[author]=name
Et voilĂ !
Conclusion
Je pense que vous l'avez vu avec les exemples mais on a une API hyper versatile. On a suffisamment de fonctionnalités pour pouvoir l'utiliser selon n'importe quelle stratégie de consommation. Et ça c'est vraiment bien parce que ça veut dire que vous pouvez exposer cette API directement à n'importe quelle équipe front et elle satisfera leurs besoins.
Et l'avantage pour vous, dev back qui développez l'API, c'est que vous pouvez confortablement rester dans le modÚle orienté objet / CRUD que Rails vous enjoint fortement à respecter, sans avoir à tordre le framework dans tous les sens.
Sauf qu'il manque encore pas mal de choses. Déjà , dans tout l'article je vous ai dit "mais ça on en reparlera plus tard", parce qu'on s'était fixé à la base d'avoir non seulement une API flexible pour le client, mais aussi customisable en terme d'autorisations et pour l'instant on a pas vraiment vu de systÚme d'autorisations. Et il y a aussi les actions de création et d'édition qui nous restent à voir.
Alors oui, on en reparlera, c'est promis, dans la prochaine édition de ce Ruby Biscuit.
D'ici lĂ , profitez bien de ce que vous avez, essayez de nous pardonner ce petit suspense.. A dans un mois, on a hĂąte !
â Thomas
Lâagence de recrutement pour les leads dĂ©vs RoR !
Comme vous le savez, derriĂšre Ruby Biscuit, il y a Capsens đ , nous sommes une agence web qui fait du Ruby on Rails depuis 10 ans.
Avec le temps on s'est rendu compte que beaucoup de dĂ©vs choisissent leur entreprise un peu par hasard alors qu'ils pourraient davantage s'Ă©panouir et se valoriser dans des structures qui leur correspondent mieux. De plus on sait Ă quel point les process de recrutement peuvent ne pas ĂȘtre adaptĂ©s Ă notre mĂ©tier et nos profils.
Ce qui tombe super bien c'est que chez Capsens nous avons une excellente connaissance de l'écosystÚme RoR en France, avec un réseau d'entreprises considérable. La plupart étant des boites bien installées (+ de 5 ans), avec des équipes tech déjà présentes et qui recherchent avant tout des leads dévs et dévs séniors.
C'est pourquoi nous avons dĂ©cidĂ© de mettre Ă profit nos ressources pour vous aider Ă trouver le poste de vos rĂȘves !
Alors tu as plusieurs annĂ©es dâexpĂ©riences ? Tu souhaites trouver le prochain poste de lead dĂ©v de tes rĂȘves ?
ConcrĂštement voilĂ ce qui va se passer :
Réponds à cette newsletter en te présentant en deux lignes !
Je tâenvoie aussitĂŽt notre test technique pour Ă©valuer ta sĂ©nioritĂ©
Je te propose des crĂ©neaux pour un appel afin de faire ta connaissance et que tu me dises ce que tu cherches pour tâĂ©panouir dans une entreprise.
Je te propose 3 entreprises qui correspondent Ă ton profil et tes aspirations. Pour chacune de ces entreprises :
Je me charge de te donner un max dâinfos et rĂ©pondre Ă toutes tes questions par message (horaires, ambiance, taille et sĂ©nioritĂ© de lâĂ©quipe, responsabilitĂ©s, marge de manĆuvre pour la nĂ©gociation du salaire, localisation des bureaux, politique de tĂ©lĂ©travail, etc). Pas dâappels inutiles.
Avant de rencontrer le recruteur lui-mĂȘme, je te mets en relation avec un dĂ©veloppeur de leur Ă©quipe. Tu pourras alors te faire une idĂ©e de comment ça se passe de lâintĂ©rieur.
Enfin, le recruteur te recevra ! Il aura dĂ©jĂ eu toutes les informations que je lui aurai transmises sur toi ce qui vous permettra dâaller Ă lâessentiel !
Lance-toi, on attend ton e-mail ! Et si tu aimes dĂ©jĂ ton travail, ne nous contacte surtout pas ! Ou alors fais-le pour nous recommander ta boĂźte đ
Thomas & Mélanie
TrÚs intéressant et utile ! Comme nombreux de vos articles !
Mais pour ĂȘtre encore plus utile, il faudrait ĂȘtre capable de copier/coller votre code ... Imaginez une seed, ou un controller Ă recopier ?! (Bon sans Copilot evidemment ... ^^)
Article sur l'Api trÚs intéressant. Je veux bien le test par curiosité.