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

Sans trop rentrer dans les détails, voici la liste des commandes que nous allons utiliser :

  • jsr clean:js pour supprimer le résultat de la transpilation contenu dans dist/js
  • jsr compile:js pour transpiler le code contenu dans src/js vers dist/js (avec en dépendance la tache de nettoyage) en utilisant Babel avec les options précisées dans le fichier de configuration.

Pour installer threerest nous allons l’ajouter au package.json :

{

  "name": "bdtheque_threerest",

  "description": "mon application de demo pour une gestion des BDs avec le framework threerest",

  "version": "0.0.1",

  "private": true,

  "dependencies": {

    "express": "^4.13.4",

    "threerest": "1.0.4"

  },

  "scripts": {

    "start": "jsr compile:js & node ./dist/js/app"

  }

} 

Il est également possible de le faire de manière automatique avec la commande

npm install threerest --save

Il suffit alors, de faire un npm install et voici express et threerest installés dans votre application.

Veuillez noter la balise scripts dans le package.json. Cette balise permet de lancer des scripts. Ici le script start va compiler votre projet puis va lancer l’app transpilée. Il faudra donc taper npm start et non plus node app.js pour lancer votre serveur node.

Afin de fonctionner pour le mieux nous allons découper nos services par unité fonctionnelle. Nous allons donc ajouter un répertoire services au même niveau que database. Nous allons aussi ajouter un fichier vide nommé serviceTest.js.  Pour que celui-ci fonctionne, nous allons importer les spécificités de threerest.

import { Service } from "threerest";

import { Methods } from "threerest";

import { Hal } from "threerest";

Le décorateur Service permet d’indiquer que telle classe représente tel service, par exemple : 

@Service.path("/test")

export default class ServiceTest {

…

}

Ici la classe ServiceTest sera le service pour la ressource test. Cette classe va nous permettre de voir le fonctionnement de base de threerest.

Les décorateurs Methods permettent d’associer une méthode de notre service à un verbe HTTP sur l’url de notre ressource. Il est donc également possible de déclarer un complément de route au sein de ce décorateur. Par exemple :

  @Methods.get("/")

  test() {

    return "it's work !!!";

  }

Donc l’URI pour obtenir la ressource test sera /test. 

Le dernier décorateur, mais pas le moins important, est Hal. Il va permettre de rendre son API hypermedia en ajoutant des liens sur son service. 

  @Methods.get("/")

  @Hal.halServiceMethod()

  test() {

    return "it's works !!!";

  }

Dernière manipulation mais non des moindres à faire avant de lancer notre service. Il faut référencer celui auprès de l’application afin de l’instancier. La manipulation se fait dans le fichier app.js

import * as ServiceTest from "./services/serviceTest";

…

threerest.ServiceLoader.loadService(app, new ServiceTest.default());

Pour rappel, la commande pour lancer votre serveur sera cette fois, npm start. La réponse pour un service qui possède ces trois décorateurs (url http:\\localhost:8080\test) sera la suivante :

{

    "_links": {

        "self": {

            "href": "/test"

        }

    },

    "data": "it's work !!!"

}

Si l’on utilise les décorateurs Hal de threerest, la réponse du service sera décorée de la manière suivante. Le résultat brut du service est mis dans la partie data et la partie link porte les liens hypermedia. Dans le cas contraire, le service revoit la valeur directement.

Donc nous avons maintenant créé un service qui nous renvoie notre donnée ainsi qu’un unique lien nous indiquant où l’on se trouve. Allons un peu plus loin à la découverte de ce framework.

Chaque ressource de vos API va faire l’objet d’une classe JS. Prenez l’exemple suivant :



@Hal.halEntity("/authors/:id")

class Author {




  @Hal.resourceId()

  id = 0;




  constructor(id, firstName, lastName, series) {

    this.id = id;

    this.firstName = firstName;

    this.lastName = lastName;

    this.series = series;

  }

}

Le décorateur @Hal.halEntity permet de définir la classe Author comme une entité du modèle Hal. Le chemin entre parenthèse permet de définir la route vers la ressource.

Le décorateur @Hal.resourceId indique l’identifiant à utiliser pour gérer le lien de cette ressource. Il est donc important de ne pas oublier ce décorateur.

L’intérêt de déclarer les objets métier de cette manière va être de pouvoir générer les liens pour l’ensemble des ressources/sous-ressources de votre API. Donc il va falloir créer un objet Author, un Serie et un Title pour chaque ressource de notre Bdthèque.

Il est maintenant temps de créer notre service, qui va nous renvoyer une liste d’auteurs. Ce sera fait dans le fichier serviceAuthors. N’oubliez pas d’instancier votre service dans l’app.js comme précédemment.

  @Methods.get("/:id")

  @Hal.halServiceMethod()

  getSwitchId(value) {

    var id = value.id;

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

    if (result) {

                   return BdHelper.getAuthor(result, id);

    }

    throw new NotFoundError();

  }




L'interrogation de la ressource /authors/2 va nous renvoyer le flux suivant :




{

    "_links": {

        "self": {

            "href": "/authors/2"

        }

    },

    "data": {

        "_links": {

            "self": {

                "href": "/authors/2"

            }

        },

        "firstName": "Christophe",

        "id": "2",

        "lastName": "Bec",

        "series": [

            {

                "_links": {

                    "self": {

                        "href": "/series/1"

                    }

                },

                "idSerie": 1,

                "name": "Carthago"

            },

            {

                "_links": {

                    "self": {

                        "href": "/series/2"

                    }

                },

                "idSerie": 2,

                "name": "Deepwater Prison"

            },

            {

                "_links": {

                    "self": {

                        "href": "/series/3"

                    }

                },

                "idSerie": 3,

                "name": "Meilleur Job du monde (le)"

            },

            {

                "_links": {

                    "self": {

                        "href": "/series/4"

                    }

                },

                "idSerie": 4,

                "name": "Prométhée"

            },

            {

                "_links": {

                    "self": {

                        "href": "/series/5"

                    }

                },

                "idSerie": 5,

                "name": "Temps des loups (Le)"

            }

        ]

    }

}

Afin d’obtenir le résultat suivant n’oubliez pas d’annoter votre classe Serie comme votre classe Author.

La pagination lui tombe sur la tête

Threerest permet aussi de gérer la pagination de façon simple. Il suffit d’ajouter un boolean à true au sein du décorateur @Hal.halServiceMethod(). Donc si votre service renvoie une liste celle-ci sera automatiquement paginée. Par défaut, threerest se base sur les mots clés pageSize et pageIdx. Le mot clé pageSize indique le nombre de résultat que le service doit renvoyer. PageIdx indique la position de début dans la liste. Naturellement si vous souhaitez utiliser d’autres mots clés, il est tout à fait possible de les customiser en les précisant dans le décorateur.

L’exemple porte sur la liste des titres de la base de données.

Voici la déclaration par défaut :

@Service.path("/titles")

export default class ServiceTitles {




  @Methods.get("/")

  @Hal.halServiceMethod(true)

  getAll() {

    return BdHelper.getTitles(db);

  }

}

Et celle en spécifiant les mots clés à utiliser :

@Service.path("/titles")

export default class ServiceTitles {




  @Methods.get("/")

  @Hal.halServiceMethod(({pageSize:"limite", pageIdx:"index"}))

  getAll() {

    return BdHelper.getTitles(db);

  }

}

Du coup pour le premier cas il vous suffira d’utiliser la requête suivante pour paginer votre service :

/titles?pageSize=2&pageIdx =14  (cela renverra deux titres à partir de la position 14)

Pour le second cas on aura la requête :

/titles?limite=2&index=14  (qui aboutira au même résultat).

Si vous voulez tester cette requête avec Httpie la requête sera la suivante :

http http://localhost:8080/titles pageSize==2 pageIdx==14

Nous obtenons exactement le même flux Json dans les cas. Voici le flux de réponse :

{

    "_links": {

        "first": {

            "href": "/titles?pageSize=2&pageIdx=0"

        },

        "last": {

            "href": "/titles?pageSize=2&pageIdx=22"

        },

        "nextLink": {

            "href": "/titles?pageSize=2&pageIdx=15"

        },

        "previousLink": {

            "href": "/titles?pageSize=2&pageIdx=13"

        },

        "self": {

            "href": "/titles?pageSize=2&pageIdx=14"

        }

    },

    "data": [

        {

            "_links": {

                "self": {

                    "href": "/titles/28"

                }

            },

            "author": "Denis Bajram",

            "id": 28,

            "name": "Le patriarche"

        },

        {

            "_links": {

                "self": {

                    "href": "/titles/29"

                }

            },

            "author": "Denis Bajram",

            "id": 29,

            "name": "Intégrale"

        }

    ]

}

 

Veuillez noter l’ajout des liens first, last, next et prev dans la partie lien Hypermédia. Cela permettra à votre client de mieux s’y retrouver dans la liste. Si jamais un élément est ajouté dans la liste en cours de pagination, il sera pris automatiquement en compte par le framework si vos données côté serveur sont à jour.

Conclusion

Nous avons donc, grâce à threerest et les décorateurs, fourni une API plus flexible à l’aide des liens hypermédia et géré de façon simple et rapide les paginations hypermédia. L’idée principale étant que la partie hypermedia est une surcouche à votre service rest et doit être couplé au minimum avec le côté HAL. Il y a donc un minimum de cohésion entre threerest et votre service.

En espérant que cela vous a donné envie de vous lancer le grand bain de l’hypermédia !

Bibliographie

Les sources de tous les exemples de l’article sont présentes sous https://github.com/lynchmaniac/bdtheque

Le projet est appelé bdtheque et regroupe un ensemble de sous-projets.

  • bdtheque_simple correspond à la partie « NodeJS chez les développeurs »
  • bdtheque_express correspond à la partie « Express et compagnie »
  • bdtheque_routes correspond à la partie « Tour de Gaule de notre API »
  • bdtheque_threerest correspond à la partie « Le domaine d’HATEOAS »

Le framework Threerest est disponible sous https://github.com/wmoulin/threerest

Et Joobster sous https://github.com/wmoulin/joobster

Une explication sur les décorateurs JS: https://blog.hadrien.eu/2015/06/05/decorateurs-es7/

Le modèle de maturité de Richardson : http://martinfowler.com/articles/richardsonMaturityModel.html

Le requêteur HTTPie : https://github.com/jkbrzt/httpie

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 !