Créer une API Hypermedia avec Threerest et Node.js en moins de 10 minutes - Partie 2

Tour de Gaule de notre API

Le projet sera nommé bdtheque_routes. Pour accéder à notre ressource authors (liste des auteurs présents dans la base), il est nécessaire de créer une route. Une route représente le chemin pour atteindre notre ressource. Plus simplement, il s’agit de l’url permettant d’interroger notre ressource.

Trois paramètres sont nécessaires pour obtenir notre ressource :

  • Une méthode contenant le code à exécuter
  • Une URL spécifique
  • Un verbe http indiquant l’action à effectuer

Selon l’architecture REST, le verbe http permet de connaitre l’action à effectuer côté serveur. Un DELETE est assez explicite ainsi qu’un GET. La fameuse différence entre POST et PUT s’explique assez simplement. Si vous savez sur quelle ressource précisément agir que cela soit pour un remplacement, une modification ou même une création, il faut utiliser le PUT. Pour les autres cas le POST est là. Il faut aussi signaler que le PUT est idempotent contrairement au POST. Toutes les requêtes PUT vont renvoyer la même réponse de la part du serveur ce qui n’est pas le cas d’un POST.

L’URL permet d’avoir l’emplacement exact de la ressource. La méthode implémente la représentation de la ressource que nous voulons renvoyer. Voici comment cela peut fonctionner.

La création d’une route en Express se résume au bout de code suivant :

// Création de la route pour consulter la liste des auteurs.

app.get('/authors', function(req, res) {

                    res.json(db['authors']);

});

Le premier paramètre de la méthode GET est l’URL à saisir pour atteindre la ressource. Si vous souhaitez faire du post ou du put il vous faudra faire un app.post ou un app.put. Attention à bien respecter la sémantique des verbes HTTP. Le REST repose sur un ensemble de normes, il est facile techniquement de passer outre, mais vous ôterez tout intérêt à votre API. Surtout si celle-ci est publique.

Le corps de la méthode se contente de récupérer la partie authors du fichier json et de la renvoyer au client.

partie authors du fichier json

partie authors du fichier json

Vous pouvez donc obtenir la liste complète des auteurs. Sur la capture, vous apercevez les auteurs d’id 4 et 5 à savoir Enki Bilal et Franck Miller (tous les exemples donnés ici sont issus de ma BDthèque personnelle)

Bien sûr si vous ne voulez récupérer qu’un seul auteur en fonction de son id, le code sera légèrement différent. Pour cela il suffit d’ajouter une nouvelle route à Express :

// Création de la route pour consulter un auteur à partir de son id.
app.get('/authors/:id', function(req, res) {

                    var id = req.params.id;

                    var result = searchParams(db, 'authors', 'id', id);

                    if (result) {

                                       res.json(result);

                    } else {

                                       // Si la liste est vide on renvoie un 200 avec un message d'explication

                                       res.json(errors['empty_list_authors']);

                    }

});

La méthode searchParams est purement utilitaire et renvoie la partie de la base de données qui nous intéresse. La partie cruciale est le passage de l’id par le chemin de l’API et la récupération par l’intermédiaire du code :

var id = req.params.id

A noter que la base de données errors contient le type d’erreur possible et renvoie le flux Json correspondant.

base de données errors

base de données errors

Nous avons donc vu ici comment récupérer l’ensemble des auteurs présents en base puis comment récupérer un auteur de façon unitaire. Mais pour cela vous avez vu que nous avons dû saisir les routes pour chacun et ainsi parcourir du code afin de connaître les routes à utiliser. Il est maintenant grand temps d’introduire un peu d’hypermédia dans notre API.

Le domaine d’HATEOAS

La maturité d’une API correspond à la manière dont les contraintes de l’architecture REST sont respectées. Cette maturité peut être représentée par un modèle en quatre niveaux. Il s’agit du modèle de maturité de Richardson.

L’idée de cet article n’est pas de vous expliquer ce modèle (pourtant crucial pour comprendre les API) mais disons qu’en résumé les quatre niveaux correspondent à ceci :

  • Le protocole d’échange (en général HTTP) sert juste de tunnel. Il fait passer les requêtes et les réponses à travers ce tunnel sans utiliser ce protocole pour indiquer l’état de l’application. C’est le niveau 0.
  • La notion de ressource doit être introduite au sein de l’API. En d’autres termes, il faut rationaliser son API. C’est le niveau 1.
  • L’emploi des verbes HTTP et des codes retours doit se faire de façon judicieuse. Chaque verbe et chaque code retour a une signification précise et doit être utilisé intelligemment. C’est le niveau 2.
  • L’introduction de l’Hypermedia apporte souplesse et puissance à son API. C’est le niveau 3.

Ce découpage est résumé par l’image suivante :

modèle de maturité de Richardson

modèle de maturité de Richardson

Le niveau 0 de Richardson est à proscrire pour vos API dans la mesure où l’on n’utilise pas le protocole de façon complète. De plus, l’absence d’utilisation de verbe et de découpage en ressource, rend votre API difficilement utilisable par vos clients. Actuellement en 2016, votre API doit au minimum respecté le niveau 2 de Richardson. Mais ici dans cet article nous allons viser le niveau 3 et comprendre ce qu’implique l’acronyme HATEOAS (Hypertext As The Engine Of Application State).

Ce mot compliqué recouvre une notion toute simple, mais extrêmement puissante. Il s’agit d’amener le concept de lien au sein de votre API. Et là vous avez complété de vous-même, on ajoute donc des liens hypermédias dans nos réponses. Pour comprendre la puissance d’un tel concept, il suffit d’imaginer une page web sans aucun lien hypertexte. Vous êtes bien embêté pour naviguer. Grâce à ces liens vous allez pouvoir maintenant naviguer dans votre API comme sur un site web.

L’importance de l’Hypermédia est de rendre une API découvrable et navigable de façon autonome. Cela va donc rendre votre API plus flexible dans la mesure où le client sera en capacité à partir d’un lien unique de naviguer dans toutes vos APIs.

Il s’agit là d’un tour très bref de la théorie autour des API REST. La notion d’hypermédia est assez vaste pour y consacrer un livre en entier. Mais pour l’article, nous allons faire court. Vous retrouvez un ensemble de liens en fin d’article, qui récapitulent les normes et bonnes pratiques autour de REST. Mais l’idée ici est de coder notre API. Alors, maintenant codons !

Le combat de l’implémentation

Pour notre BDthèque cela va se manifester par la possibilité à partir d’un auteur de naviguer vers ses séries puis vers les titres des séries. La navigation se faisant dans un sens bidirectionnel.

L’hypermédia a de nombreuses spécifications (Siren, Hal, Json-API, Hydra, Json-LD…). Actuellement, cette abondance de spécifications est un frein au développement de l’hypermédia. Ces spécifications servent à fournir un format commun d’échange et de représentations des liens permettant ainsi l’interopérabilité plus aisée. Il est donc primordial de bien s’accorder sur la spécification à utiliser.

Le format HAL est le plus globalement adopté et se démarque de ses concurrents par sa simplicité. De plus, la communauté HAL est de loin la plus importante, il sera donc plus facile de trouver ressource et aide auprès de celle-ci. Nous allons donc nous fier à cette spécification afin d’implémenter notre API hypermédia. Rassurez-vous, globalement la différence porte sur la façon d’implémenter nos liens, donc la marche entre les différentes spécifications n’est pas énorme.

Une ressource auteur de BD ressemble à ceci :

{

        "firstName": "Denis",

        "id": "3",

        "lastName": "Bajram",

        "series": [

            {

                "idSerie": 6,

                "name": "Expérience mort"

            },

            {

                "idSerie": 7,

                "name": "Universal War One"

            },

            {

                "idSerie": 8,

                "name": "Universal War Two"

            }

        ]

    }

} 

L’équivalent en mode « HAL » correspond au flux Json suivant :

{

    "_links": {

        "self": {

            "href": "/authors/3"

        }

    },

    "data": {

        "_links": {

            "self": {

                "href": "/authors/3"

            }

        },

        "firstName": "Denis",

        "id": "3",

        "lastName": "Bajram",

        "series": [

            {

                "_links": {

                    "self": {

                        "href": "/series/6"

                    }

                },

                "idSerie": 6,

                "name": "Expérience mort"

            },

            {

                "_links": {

                    "self": {

                        "href": "/series/7"

                    }

                },

                "idSerie": 7,

                "name": "Universal War One"

            },

            {

                "_links": {

                    "self": {

                        "href": "/series/8"

                    }

                },

                "idSerie": 8,

                "name": "Universal War Two"

            }

        ]

    }

} 

Pour vous présenter et simplifier tout ça, nous allons nous appuyer sur le Framework threerest. Il est issu d’une idée pour un autre projet, et créé en étroite collaboration avec Wilfried Moulin, Expert technique à SQLI Nantes. En gros, il permet de créer des services REST de niveau 2 et peut gérer des notions d’hypermédia.

Donc le Framework threerest existe et va nous aider dans cette tâche. Les sources sont disponibles sous https://github.com/wmoulin/threerest. Ce Framework est basé sur les décorateurs (services REST, méthode HTTP, hypermédia…). Si vous ignorez ce que sont les décorateurs en JS, je vous renvoie vers l’article suivant : https://blog.hadrien.eu/2015/06/05/decorateurs-es7/.

Il faudra donc travailler sur une version de spécification d’EcmaScript pas encore implémentée dans NodeJs et par aucun navigateur (au moment de la rédaction de l’article). De plus, dans nos différents exemples, nous allons utiliser la syntaxe de module et de classe ES6. Pour se faire, nous allons donc devoir transpiler le code en une version d’EcmaScript supportée par ce dernier.

Là aussi pour nous simplifier la tâche, nous nous sommes appuyés sur un outil basé sur Gulp et créé par Wilfried Moulin : Joobster.

Joobster est un facilitateur qui a donc pour but de faciliter la vie du développeur JS en prenant en charge les parties de transpilation, de test et de packaging. Ce facilitateur est en cours de construction et pour l’instant quelques premières fonctionnalités sont implémentées, mais cela va suffire à notre bonheur. Pour l’installer, il suffit de taper l’instruction suivante:

npm install joobster -g

Cela va installer globalement cet utilitaire. Pour pouvoir gérer plus facilement la transpilation, ce framework se base sur une structure prédéfinie. Il est possible d’utiliser une structure propre, mais pour cela il faudra la préciser dans un fichier joobster.json à la racine du projet.

Nous allons donc créer un nouveau projet nommé bdtheque_threerest. La structure va être la suivante :

bdthèque threerest

bdthèque threerest

Vos sources seront dans src et tst et votre code transpilé sera recopié dans dist. Le code à exécuter sera donc dans dist.  Voici le contenu du fichier joobster.json :

{

  "javascript": {

    "compile" : {

      "presets": ["es2015", "stage-1"],

      "plugins" : ["transform-decorators-legacy"]

    }

  }

}

 

0 commentaires

votre commentaire

Se joindre à la discussion ?
Vous êtes libre de contribuer !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Inscription newsletter

Ne manquez plus nos derniers articles !