Une API Design-first en Node.js avec Fastify
Présentation de l'approche Design-first par l'exemple
Une API Design-first en Node.js avec Fastify
Objectif
Dans cet article je vous propose de coder pas à pas une API Rest en Node.js et en mode “Design First”, c’est à dire que nous allons coder un contrat d’interface au format OpenAPI plutôt que de coder l’implémentation de notre API (les routes, les contrôleurs, la validation des entrants…).
On va s’appuyer sur la stack technique suivante :
- Node.js v18.3.0 / Typescript / ECMAScript module
- Fastify : Framework web inspiré de Hapi et Express, qui met l’accent sur la simplicité et la performance
- Fastify OpenApi Glue : Plugin Fastify qui permet d’auto-générer une API depuis un contrat d’interface OpenAPI (v2/v3)
- Fastify Swagger : Plugin Fastify qui permet d’exposer sur l’API le contrat d’interface OpenAPI
- Fastify Swagger UI : Plugin Fastify fournissant une interface Swagger UI permettant de visualiser et tester l’API
Design First vs Code First
Premièrement il me faut vous parler de l’approche “Design First” (aussi appellée “API First”) qui nous intéresse ici. Pour ceci je ne saurai que vous conseiller d’aller lire l’article suivant qui explique et compare les 2 approches :
🔗 https://swagger.io/blog/api-design/design-first-or-code-first-api-development/
En gros il faut bien cerner vos besoins concernant l’API que vous voulez coder.
Ce que j’en retire personnellement comme conclusions :
- C’est que l’approche “Design First” fluidifie la collaboration avec les utilisateurs de votre API (autre équipe de dev / client) dans le sens où les ajustements se font directement sur le contrat d’interface plutôt que dans le code de votre application, ce qui rend plus simples et plus rapides les évolutions à apporter à l’API.
- De plus cette approche permet de se concentrer uniquement sur le design de l’API sans avoir de biais liés à l’implémentation, et donc de s’assurer qu’elle conviendra parfaitement à vos clients.
Par contre il se peut que vous trouviez fastidieux d’adapter le code de l’application au fur et à mesure des évolutions du contrat d’interface.
Mais pas de panique il existe des outils pour vous aider à mettre en pratique cette approche “Design First” 😃
OpenAPI
Ok c’est parti on commence donc par le contrat d’interface.
La spécification qui s’est imposée pour décrire des contrats d’interface est la norme OpenAPI Specification. C’est la suite de la la spécification Swagger, proposée par SmartBear Software, depuis 2015 elle est sous l’égide de la OpenAPI Initiative.
On peut la décrire comme suit :
La spécification OpenAPI (OAS) définit une interface standard indépendante du langage pour les API RESTful qui permet aux humains et aux ordinateurs de découvrir et de comprendre les capacités du service sans accéder au code source.
➡️ Concrètement, elle prend la forme d’un document JSON
ou YAML
.
Si vous êtes un fou furieux vous pouvez ouvrir votre IDE favori et implémenter directement le code de votre contrat d’interface… Sinon il y a plusieurs outils qui vont vous faciliter cette étape.
Il y a tout d’abord le classique Swagger Editor dispo en ligne qui est bien pratique avec son volet de visualisation du rendu dans une instance Swagger UI. Par contre là encore il vous faut coder à la main le contrat d’interface.
Pour ceux qui ne maitrisent pas encore la syntaxe OpenAPI il existe des outils plus complets qui permettent de switcher entre le code, et un mode de saisie via une IHM.
Personnellement j’ai testé et vous recommande Stoplight Studio.
Démo
Je vous propose d’illustrer tout ça avec un petit projet basique d’API en Node.JS
Etape 1 : Initier un serveur Fastify
Tout d’abord il faut installer Fastify
Avec npm :
|
|
Avec yarn :
|
|
Je vous propose d’implémenter le serveur Fastify en Typescript, dans un fichier src/app.ts
comme suit :
|
|
Ensuite avant de lancer le serveur, on va ajouter un peu de configuration en rapport avec notre cible qui est la suivante :
- Le projet sera codé en Typescript
- Le packaging de l’appli sera en ECMAScript module
Les deux scripts suivants permettent de builder le code Javascript à partir de sources Typescript, et de démarrer le serveur Fastify via ts-node.
Ils sont à ajouter dans le fichier package.json
:
|
|
Pour packager correctement le module, il est nécessaire d’installer également les dépendances suivantes :
|
|
puis d’ajouter également la configuration suivante dans le fichier package.json
:
|
|
Et enfin on ajoute le fichier de configuration Typescript tsconfig.json
suivant :
|
|
🚀 Désormais le serveur Fastify se lance correctement via la commande suivante :
|
|
Plus d’infos pour bien démarrer avec Fastify ici :
🔗 https://www.fastify.io/docs/latest/Guides/Getting-Started/
🔗 https://www.fastify.io/docs/latest/Reference/TypeScript/
Etape 2 : Implémenter le contrat d’interface
Maintenant qu’on a un serveur web qui tourne, on va implémenter notre API…
Mais avec l’approche “Design First” bien entendu 😁
J’ai repris le contrat d’interface proposé sur le Github de fastify-openapi-glue que j’ai un peu allegé :
|
|
Vous pouvez éditer ce document via les outils cités préalablement.
Dans ce contrat d’interface on retrouve de la validation de donnée via JSON Schema.
Vous noterez aussi les champs operationId
sur chaque endpoint exposé.
Ces champs seront utilisés pour pointer vers les bonnes méthodes de la classe où seront codées les implémentations fonctionnelles.
Pour implémenter les endpoints décrits dans cette spécification nous allons ajouter à notre application le plugin Fastify OpenApi Glue :
|
|
Ce plugin permet de générer toute la configuration Fastify des différents endpoints, ainsi que les contrôles de validation des données entrantes. Ce qu’il reste à coder est l’implémentation du service, à savoir uniquement le code métier de l’API 😎
La classe suivante déclare des méthodes nommées en reprenant la valeur des champs operationId
des différents endpoints du contrat d’interface.
|
|
Dernière étape pour faire fonctionner ce plugin, l’enregister auprès du serveur Fastify, et lui indiquer où se trouve la spec OpenAPI :
|
|
Vous pouvez tester les différentes routes pour vérifier que vous pointez bien vers les bonnes méthodes :
|
|
Là où c’est intéressant à cette étape, c’est que vous n’avez encore rien codé de votre logique métier, et que les contrôles de validation sont déjà opérationnels.
Vous pouvez le constater via la reqûete suivante par exemple :
|
|
Etape 3 : Exposer le contrat d’interface sur l’API et ajouter l’interface Swagger-UI
Une idée intéressante désormais est d’exposer le contrat d’interface au format OpenAPI dans votre API elle-même.
Cela est très pratique pour les utilisateurs de votre API, en effet à chaque redéploiement de votre API, la spec qui correspond au code est accessible au même endroit.
Cette bonne pratique simplifie la collaboration avec vos utilisateurs.
Autre bonne idée, fournir une interface web Swagger-UI basée sur le contrat d’interface et simplifiant encore plus l’utilisation de votre API. C’est vrai que c’est plus simple d’utilisation qu’une collection Postman ou d’écrire des requêtes Curl.
L’installation des plugins Fastify Swagger et Fastify Swagger UI permet de bénéficier de ces fonctionnalités :
|
|
Ne pas oublier d’activer ces nouveaux plugins au serveur Fastify :
|
|
Désormais, vous pouvez vous rendre à la page http://localhost:3000/documentation
pour accèder à l’interface Swagger-UI et ainsi tester vos différents endpoints facilement 😎
Conclusion
Voilà vous pouvez désormais continuer d’avancer sur 2 axes en parallèle :
- Implémenter la logique métier de votre API dans la classe
RouteHandler.ts
- Itérer avec vos utilisateurs sur la spec OpenAPI :
- Designer des nouveaux endpoints
- Documenter un max les différents paths et modèles de donnée, en décrivant de manière la plus précise possible les modèles de donnée (format, utilisation d’énumérations, des champs
description
… - Ajouter des exemples de requêtes sur chaque Path
Le projet complet présenté dans cet article est à retrouver ici : https://gitlab.com/gasouch/petstore-design-first
À propos de l'auteur : Mathieu Souchet
Développeur Full-Stack depuis 2007 (à forte teneur en Java), j’ai rejoint l’aventure Max en janvier 2017. Mon parcours m’a amené à collaborer au plus près de mes clients, leur apportant expertise technique, méthodologique et sens du service.
Touche-à-tout, passionné de technique, je m’épanouis en découvrant de nouvelles technos dans un contexte Agile et dans la bonne humeur.