Skip to content

Instantly share code, notes, and snippets.

@pixelastic
Created April 27, 2015 16:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pixelastic/dcc07ca391ec93e70eb0 to your computer and use it in GitHub Desktop.
Save pixelastic/dcc07ca391ec93e70eb0 to your computer and use it in GitHub Desktop.

Rédaction

Table of Contents

    1. Intro
    1. Why do an API in Node?
    1. Hapi et Express en principe
    • 3.1. Express
      • 3.1.1. Philosophie
      • 3.1.2. Historique
      • 3.1.3. Rythme de développement
      • 3.1.4. Communauté
    • 3.2. Hapi
      • 3.2.1. Philosophie
      • 3.2.2. Historique
      • 3.2.3. Rythme de développement
      • 3.2.4. Communauté
    • 3.3. Synthèse et Statiques
    1. Express et Hapi en pratique
    • 4.1. Intro
      • 4.1.1. Code.show()
      • 4.1.2. Description use case exemple
    • 4.2. Les composants principaux
      • 4.2.1. Au coeur, le serveur
      • 4.2.2. Définition des routes
      • 4.2.3. Les handleurs de requêtes
    • 4.3. Interprétation de la requête
      • 4.3.1. Diversité des données à récupérer
      • 4.3.2. Processus de collecte des corps
      • 4.3.3. Quelques pointeur vers du concret
    • 4.4. Création de la réponse
      • 4.4.1. Codes de Retour HTTP
      • 4.4.2. Utilisation des Headers HTTP
      • 4.4.3. Gestion du Corps du message
    • 4.5. TODO Express et Hapi au delà du basique
    1. Conclusion: why we focus on Hapi

1 Intro

Ces dernières années l'approche REST est être train de devenir l'architecture incontournable des API. Parallèlement on assiste à une reconfiguration du paysage coté server notamment avec la percée de Node.js qui s'est imposé comme un des principale stack technique.

Dans cette article nous allons creuser comment faire de telles API REST en node.js, ceci au travers des deux frameworks principaux, Express et Hapi.

On présentera dans un premier temps ce qui fait que node.js est une stack technique attrayante pour réaliser les Api, avant de voir les deux frameworks, tant d'un point de vue principes, que d'un point de vue pratique.

Si jamais vous êtes trop impatient de voire du code et de lire la partie technique, vous pouvez toujours passer par ce petit raccourci.

2 Why do an API in Node?

Node est attrayant pour la réalisation de services REST pour plusieurs raisons:

Tout d'abord, Nodejs est nativement très orienté Web, la bibliothèque de serveur HTTP étant intégrée, ainsi que les protocoles de plus bas niveau. De plus json étant devenu le format d'échange standard sur HTTP, format on ne peut plus facile à produire en javascript.

Node.js a bien d'autres atouts pour la réalisation de telles API. Node.js pousse à réaliser des applications modulaires, donc facile à répartir entre plusieurs équipes.

De plus celui-ci est léger, interprété et réactif ce qui permet de fluidifier les développements, et de pratiquer le déploiement continue.

Pour autant ceci ne se fait pas au dépend de la performance, l'approche asynchrone de boucle d'événement au coeur de node et la machine virtuelle v8 ayant fait leurs preuves dans nos navigateur. La scalabilité se fait aisément de manière horizontale sans développement supplémentaire.

Aussi la plateforme est bien vivante avec un support industriel et une forte communauté avec son un environnement de plugins et frameworks considérable.

Preuve de ceci, malgré être relativement jeune, la plateforme Node a déjà été adoptée, utilisée et éprouvée par des grands du Web comme Walmart, Paypal, Linkedin, Yahoo!

3 Hapi et Express en principe

Dans l'écosystème Node, deux frameworks se dégagent quant à la réalisation de services REST. Il s'agit d'Express et d'Hapi.

Nous allons dans cette partie donner une vue d'ensemble: parcourir leur cible, leur philosophie sous-jacente ainsi que leur historique, et la communauté qui autour d'eux s'est développée.

Nous laissons de coté Koa, celui-ci étant une revisite d'express à la sauce Es6, ainsi que d'autres frameworks qui se sont construit au dessus d'express.

3.1 Express

Express est un minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. Ou comme un cadriciel minimal et flexible.

3.1.1 Philosophie

Express est une simple et expressive Api au dessus des fonctionnalités web du serveur http de node.js.

Le principal job dont se charge express est de router les requêtes aux bons middlewares.

Le framework n'est pas opiniâtre, n'imposant pas de structure rigide. Il ne s'appuie pas sur le principe Convention over Configuration, étant flexible tant pour la structure que pour les middleware utilisés.

En effet un des motos d'express est @batterie not included. Ce sont l'écosystème de middleware construit autour de connect puis express qui emmènent les fonctionnalités.

  1. Middleware

Le concept de middleware prédate express et a été introduit dans node par connect.

Le principe est que plutot d'avoir un handleur monolytique qui se charge de gérer l'intégralité d'une requête, autant avoir une collection de handleur qui se chargent de faire un traitement spécifique.

Construire un serveur revient donc à chainer les middlewares, prétraitement, accumuler information, puis traiter les différents cas. Ainsi on a des middlewares pour gérer l'authentification, logger les requêtes, parser les cookies et corps des requêtes, régler certains headers de la réponse et ainsi de suite jusqu'à l'émission de la réponse.

Un middleware est une fonction avec trois arguments. Les deux premiers items req et res sont là pour accéder aux informations de la requêtes, et pour manipuler la réponse. Le dernier est une callback next pour éventuellement passer la main au prochain middleware de la chaine.

L'ordre de ces middlewares est primordial, il faut donc bien les chainer dans l'ordre qu'il convient.

En réalité il y a deux types de middleware, ceux pour gérer le cas nominal, et ceux pour gérer les cas d'erreurs. On bascule en mode erreur dès qu'un middleware en appelant next avec un argument, l'erreur.

  1. Routage et sous applications

Comme évoqué précédement, le routage est le coeur de la fonctionnalité apporté par express.

Il s'agit d'associer à une route particulière, c'est à dire à des pattern d'url et des verbes http particulier les middlewares qui se chargeront de traiter les requêtes.

Le routage n'est pas restreint à des routes statiques, on peut avoir recours à des routes dynamiques ce qui permettra de récupérer des variables de chemin (pathvariables)

L'algorithme de routage est assez simple, pour chaque requête entrante on va suivre la chaine de middlewares, et exécuter seulement ceux dont la route va s'appareiller avec le méthode et url demandée.

A noter qu'il est aussi possible de construire des sous-applications, des routeurs que l'on viendra monter sur son serveur final.

Ceci permet ainsi d'avoir de découper son api et d'avoir des composants réutilisables, comme par exemple un module d'administration.

3.1.2 Historique

Express est le plus ancien des deux frameworks, d'ailleurs son développement a commencé en 2009 dès les débuts grand public de node. Une premiere milestone en 2010, beta juillet, suivie de plusieurs release candidate jusqu'en novembre avec publication v1.

Construit à l'origine sur connect et largement inspiré par Sinatra (Sinatra inspired web development framework, il s'est vite établis comme le framework web de référence sur la plateforme node.

Son principal developper fut tj, TJ Holowaychuk travaillant à VisionMedia notament à l'origine des bibliothèques node superagent et supertest.

Dernièrement le flambeau repris par @dougwilson, Tj se concentrant sur go, et transférant le dépot à StrongLoop (à l'origine LoopBack un des framework construit sur express)

3.1.3 Rythme de développement

Express est actuellent en version 4, la version 3 étant toujours maintenue.

En plus de ces deux branches actives, la version 5 est en préparation.

La principale différence entre la 3 et la 4 est que connect a été supprimé comme dépendance, et la plupart des middlewares intégrés ont été sorti dans des modules spécifiques.

Chaque passage de version majeure est documenté à la fois avec la liste des nouvelles foncitonnalités ([v3](https://github.com/strongloop/express/wiki /New-features-in-3.x), [v4](https://github.com/strongloop/express/wiki/New- features-in-4.x)) et un guide de migration (v2 vers v3, [v3 vers v4](https://github.com/strongloop/express/wiki/Migrating- from-3.x-to-4.x))

Quant à la v5, les version alphas sont déjà disponible sur npm, npm install express@5.0.0-alpha.1, et on peut suivre leur développement sur les branches 5.x et 5.0

Celle-ci entrainera notamment la suppression de fonctions déjà dépréciées comme res.json(obj, status), res.jsonp(obj, status), res.send(body, status), res.send(status), res.sendfile(file).

3.1.4 Communauté

De part sa philosophie nodesque, batterie not includes express lui même apporte un nombre limité de fonctionnalités par lui même. Un grand nombre de middleware a été développés pour apporter celles-c et faciliter les différents aspects que peut nécessiter la bonne réalisation d'une api rest.

Les principaux sont répertoriés sur le site officiel.

Niveau communication, en plus du site vitrine, qui contient aussi tutoriel et documentation, le projet est bien entendu hébergé sur github.

Pour engager la communauté, il existe un google group dédié ainsi qu'un channel irc #express et un chat gitter.

A noter que certain framework se sont construit sur express, notamment pour libérer le développeur de nombreuses décisions via des conventions. (comme Kraken, Sails, Locomotive ou Loopback

3.2 Hapi

Hapi est un rich framework for building applications and services.

hapi enables developers to focus on writing reusable application logic instead of spending time building infrastructure.

3.2.1 Philosophie

La philisophie d'hapi est assez différente d'express avec des choix que l'on pourrait qualifier de radicalement différents. En effet Hapi abstrait le serveur http node. Il introduit un cycle d'une vie de requête et propose une autre architecture pour favoriser l'isolation des différents composants de l'application et ainsi éviter les impacts inatendus.

Pour cela ses deux traits principaux de Hapi sont la configuration plutot que le code, et une organisation du code à l'aide d'une architecture de plugin

  1. Configuration over code

Hapi a une approche centrée configuration.

Mis à part la création des handlers de requête et leur logique métier, construire le serveur reviendra à configurer les différentes propriétés du server, les plugins chargés, et surtout les différentes routes.

La spécification de route en hapi est d'ailleurs le meilleur exemple pour illustrer cette différence d'approche avec express. Dans ce dernier, les routes sont ajoutées via différentes méthodes pour chaque verbe http, alors que dans hapi l'api offre une seule méthode qui va prendre en argument un object de configuration décrivant la route.

On retrouvera cette approche d'objet de configuration à la fois pour le serveur et les différents plugins que l'on chargera. Ceci permet notamment de changer le comportement du serveur dans les différents environnements en changeant juste la configuration. Ainsi à titre d'exemple, on pourra aisément désactiver la mise en cache en production, ne pas activer certaines surcouches comme la journalisation des requêtes pour les test. Ceci permet aussi de deployer plusieurs version de l'application, de facilement mettre en place un système de feature flipping ou de gérer l'évolution de certaines parties du serveur.

L'aspect configuration permet aussi à de nombreux préoccupation comme les performances, la robustesse et la sécurité, d'être traitées de base par le serveur tout en permettant leur paramétrage.

  1. Architecture de plugin et modularization

Hapi s'est construit en réaction à un des problèmes d'express, le fait qu'il ne passait pas à l'échelle d'un point de vue organisationnel. En effet, express permet de construire une api très vite, mais difficilement maintenable. Le montage des routes est un goulot d'étranglement ou il faut coordonner les changements au risque de surprises.

Du coup Hapi a été conçu pour offrir un cadre pour écrire des applications réutilisables de façon très modulaire, avec son système de plugin.

Un plugin est un composant logique, réutilisable, et indépendant apportant un certain nombre de fonctionnalités aux serveurs. Pour développer les fonctionnalités transverses, qui s'appliqueront à l'ensemble des requêtes, hapi donne un certain nombre de point d'entrée sur le traitement de la requête.

En effet plutot que de concevoir le traitement d'une requête par une suite de fonctions définies par l'utilisateur, Hapi définit un cycle de vie de la requête, avec ses points d'extensions associés, événements sur lequel on pourra accrocher des traitements comme la réception de la requête, la fin de l'authentification, le début d'émission de la réponse.

Il existe un bon nombre de plugin "natifs", développés par l'équipe derrière hapi, et reconnaissable (ou pas) à leur nom assez original.

Parmi les quasi indispensables il y a Joi pour la validation de schéma, Good pour le monitoring et logging (avec une extension pour le rejeu), aussi tv pour le debuggage intéractif, Bell pour l'authentification tierce.

Les plugins ne se réduisent pas aux fonctionnalités transverses, c'est un cadre pour découper le code, et c'est d'ailleurs la manière recommandée de structurer son application.

Hapi offre aussi un moyen d'injecter des dépendances, de mettre à disposition des objets et fonctionnalités à l'ensemble des parts du serveur.

  1. Une bonne synergie

Les deux approches de configuration et de plugin se nourrissent mutuellement. Ainsi Hapi offre un cadre que l'on pourra facilement étendre et configurer, cadre offrant nativement sécurité et robustesse.

De part l'aspect configuration, et le fait d'avoir des handlers en un seul bloc (en faisant abstraction des points d'extension bien défini), le routage est totalement déterministique.

Ceci est une grande force d'hapi, et surtout rend les collisions de routes détectables.

Le cas présent le serveur ne démarrera pas, et affichera une erreur explicite alors que dans express ce problème potentiel est totalement silencieux et invisible, un vrai /"Middleware hell"/.

3.2.2 Historique

Hapi est né bien plus récemment au Lab Walmart sous la houlette de Eran Hammer aka hueniverse. Le projet a commencé en aout 2012, né des frustrations et limitations de l'utilisation des outils existants. N'ayant pas trouvé un framework améliorable dans le sens souhaité, l'équipe est partie from scratch, reprenant quelques bonnes pratiques mises en place sur un précédent projet, sledge.

Le principal grief contre express étant le passage à l'échelle en terme organisationnel, le montage des routes étant le goulot d'entrainement où il était fréquent que des membres d'équipe se marchent les uns sur les autres en insérent de nouvelles routes.

Dans un premier temps basé sur express en proposant une abstraction au dessus, Hapo s'en est rapidement détaché. Après une série de version mineures, la première version majeure est sortie en avril 2013 faisant suite à la V0.13.

Hapi passe avec brio son baptème de feu lors du black friday 2013 à Walmart, sur lequel Eric Hammer est revenu dans quelques talks et entretient.

Une v2 est sortie en Jan 2014, suivant la v1.20 en raison de changement incompatibles pour raisons de sécurité.

S'en suit au cours de l'année 2014 plusieurs version majeures, en raison de changements non retrocompatibles, notamment en raison de l'extraction de nombreuses fonctionnalités dans des modules séparés et d'une refonte d'api.

La liste des milestones et versions majeures est disponible sur le dépot github via les issues bien catégorisées.

3.2.3 Rythme de développement

Ces 8 versions majeures en un lapse de temps si cours peuvent donner l'impression d'une certaine instabilité. Cependant Hapi est jeune, la SemVer pousse à vite bumper la version majeur, et surtout ces changements sont très bien documentés. D'ailleurs pour chacun d'entre eux on a le droit à une estimation du temps de mise à jour, de la complexité, des risques, et des dépendances!

La version actuelle a été un refactoring majeur du framework et de son api.

Les différentes interfaces existantes pour configurer le serveur ont été unifiées. C'est ainsi que des méthodes comme pack on disparu. (un pack étant la composition de plusieurs servers en un unique objet)

L'objectif était celui de simplifier l'expérience de développement en rendant l'api plus simple et prévisible, et ainsi gagner en simplicité et apprenabilité sans perdre en puissance.

Ce gros refactor a été fait dans l'état d'esprit des /"breaking changes worth taking"/ revendiqué par le mainteneur principal.

Les 7 versions majeures en 10 mois qu'à connue l'année 2014, de la v2 à v8 semblant participer à cette refonte progressive de l'api.

La communication sur la v8 laisse à penser qu'il est peu probable d'avoir de nouvelle version majeure dans les mois qui vienne.

A noté qu'il n'y a pas de branche de développement, en public tout de moins au moins.

A titre d'example, voici l'estimation associé à la dernière version:

Upgrade time

moderate to high - a couple of days to a week for most users

Complexity

moderate - a very long list of changes, all well understood with no side effects

Risk

moderate - no side effects but a lot of changes to keep track of

Dependencies

moderate to high - every plugin must be verified to be compatible or upgraded

On retrouvera ainsi les notes de sorties des versions majeures de l'année 2014: v8 en novembre, v7 en octobre, v6 en juin, v5 en mai, v4 en avril, v3 en mars.

3.2.4 Communauté

Bien que né a Wallmart, Hapi a su s'en détacher, et attirer une communauté conséquente autour de lui.

Néanmoins il est indéniable que son créateur garde pour l'instant un leadership certain sur la vie du framework. Cela n'a pas empêché 900 pull requests d'etre accepté, et il a 21 personnes actuellement dans l'organisation hapi gérant l'écosystème.

Le dépot initialement sur le compte de wallmartlab a été migré sur un compte spécifique, désormais appelé hapijs (anciennement spumko en référence à spunco).

Celui-ci contient bien entendu le framework hapi, mais aussi l'ensemble des plugins de l'écosystème, et le code source du site contenant la documentation et divers tutoriels.

Toute la dynamique et le développement du plugin se fait via github et ses issues ce qui permet à la fois visibilité et centralisation. Les utilisateurs sont invités à y poser ici leur questions plutot que dans un forum dédié. (à l'exception bien entendu de celles ayant leur place sur stackoverflow), les changements d'api y sont documentés. Tout cela est rendu possible par un système de tag très bien pensé des issues: bug, security, dependency, discussion, documentation, release notes, breaking changes, question.

Hapi est utilisé par de nombreuses boites commerciales d'envergure listées sur leur site: notamment Disney, mozilla, Paypal, npm.

Comme Express il dispose d'un channel IRC #hapi et d'un chat gitter.

A noter aussi l'existence d'un programme de mentoring pour assister les développeurs dans leur montée en compétence sur hapi.

3.3 Synthèse et Statiques

Après ces nombreux paragraphes un peu verbeux mais ayant le mérite de présenter la philosophie, historique, et communauté des deux frameworks, nul doute qu'un peu de chiffre permettra de se reposer les yeux.

En terme d'audience, voici quelques indicateurs qui viennent donnée une idée de l'aura des deux frameworks.

Métriques Express Hapi

Github stars

18k

4k

Github fork

3,6k

0,6k

StackOverflow

14k

180

Contributor

177

114

Github require

~360k

6k

Par contre attention, en plus de ne pas remplacer l'analyse quantitative qu'on vient de faire, ces métriques sont à prendre pour ce quelles sont. Il ne faut pas oublier de considérer que les deux frameworks n'ont pas la même ancienneté. Ce qui est bien illustré par une analyse des recherches de mots clefs sur google où on voit que bien que nouveau, hapi a bien réussi à s'implanter, et se poser en concurrent en express.

J'allais oublier, mais il y a des chances que je n'apprenne rien en disant que tous deux on un cours dédié sur nodeschool, expressworks et makemehapi (et installable depuis npm)

4 Express et Hapi en pratique

Concrètement, comment on fait?

Maintenant qu'on a vu les principes sous-jacents aux deux cadriciels, leur historique et environnement, regardons comment réaliser une belle API REST.

4.1 Intro

Pour un bon rafraichissement de ces principes, une lecture de la [liste des bonnes pratiques REST est indispensable](http://blog.octo.com/designer-une- api-rest/). On se réfèrera sinon à la [refcard OCTO](http://blog.octo.com/wp- content/uploads/2014/12/RESTful-API-design-OCTO-Quick-Reference-Card- POSTER-2.4.pdf).

4.1.1 Code.show()

Avant de rentrer plus en détails, voici un très bref aperçu des deux cadriciels, un simple hello world récupéré depuis les documentations officielles respectives.

  1. Hello Hapi
var Hapi = require('hapi');

var server = new Hapi.Server();
server.connection({ port: 3000 });

server.route({
    method: 'GET',
    path: '/',
    handler: function (request, reply) {
        reply('Hello, world!');
    }
});

server.start(function () {
    console.log('Server running at:', server.info.uri);
});

On remarquera les nombreux objets de config pris en argument.

  1. Hello Express
var express = require('express');
var app = express();

// respond with "hello world" when a GET request is made to the homepage
app.get('/', function(req, res) {
    res.send('hello world');
});

var server = app.listen(3000, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log('Example app listening at http://%s:%s', host, port);

});

4.1.2 Description use case exemple

L'api qui va nous servir de use case et qu'on va utiliser pour illustrer le propos sera "Pending-Link". L'API REST va nous permettre de stocker, bookmarker de manière temporaire un lien et de le retrouver ultérieurement.

Il s'agit d'une simple api "monoressource" supportant l'ensemble des opérations CRUD et de recherche basique.

# Créer un bookmark :
curl -X POST https://api.octo.com/v1/links \
-H 'Content-Type:application/json' \
-d '{"url":"http://www.devoxx.com/"}'

< 'Location':'/v1/links/0'

# Récupérer un bookmark :
CURL -X GET  https://api.octo.com/v1/links \
-h 'Accept:application/json' \
[{"url":"http://www.devoxx.com/"}]'

Pour plus de détails, vous trouverez ici la documentation RAML de l'api.

§LINK

4.2 Les composants principaux

Décrivons un peu ce que nous venons de voir dans ces deux salutations.

4.2.1 Au coeur, le serveur

Avant de pouvoir servir une requête, il faut bien commencer par construire un server. (et aussi requerir la librairie, mais cela va de soi :))

Une légère différence apparaît dès ce moment là, les objets ne recoupant pas exactement les mêmes concepts.

En express l'entité principale est l'application (généralement appelée app par convention), que l'on va configurer, agrémenter de routes ou middleware avant d'écouter un port particulier. C'est par cette opération listen qu'on obtient le serveur, que l'on pourra arrêter avec la méthode close prennant aussi une fonction de rappel.

En hapi il n'y a qu'une seule entité, le serveur, que l'on va configurer, sur lequel on pourra définir plusieurs connexions (avec la méthode connexion), qui seront instantiées lors du démarrage du serveur avec la fonction start (qui précédera un arrêt du server avec la méthode stop)

(à noter que dans le protoype support pending-link, le serveur express a été encapsulé pour offrir la meme interface pour avoir un main agnostic au framework utilisé)

Ces deux entités sont configurables. En hapi cela de préférence avec un objet de config passé au constructeur new Hapi.Server();.

En express l'application met à disposition des fonctions comme disable ou enable pour activer ou désactiver un certain nombre de paramètres(§link:[htt p://expressjs.com/api.html#app.settings.table](http://expressjs.com/api.html#a pp.settings.table))

4.2.2 Définition des routes

Une fois le serveur défini, la prochaine étape et de lui ajouter les routes, les urls que l'on veut gérer.

La différence d'approche décrite dans la première partie se retrouve au niveau de l'api.

  1. En express

Pour définir une route en hapi on invoquera une des méthodes get, post avec l'url matchée et le handleur spécifié. On a une méthode app.METHOD pour chaque verbe http.

Le premier argument est l'url de la route, celle-ci pouvant contenir des variables dynamiques, un identifiant précédé par un :. On peut aussi avoir recours à des expression régulière

app.get('/links/:id', LinkController.get);

Il existe une méthode all qui sera appliquée pour l'ensemble des routes, quelque soit la méthode. Elle est utilisée pour charger les middlewares.

A noter la possibilité d'invoquer ces méthodes sur un Routeur que l'on viendra ensuite monter sur l'application. (un routeur est un middleware spécifique doté de son propre système de routage)

var router = express.Router();
router.get('/', function(req, res) {
  res.send('Hellooooo');
});
// elsewhere with a different name
app.use('/some-url', router);

On préfèrera cette approche pour plus de modularité et réutilisabilité.

  1. En Hapi

A l'opposé, en hapi il y a une seule méthode du server au nom peu surprenant, route. Celle-ci prend un objet de configuration (ou un tableau de tels objets), devant contenir à minima les clefs method pour le verbe HTTP, path pour l'url géré, et handler pour la fonction en charge de la gestion des requêtes.

server.route({
    method: 'GET', path: '/hello', handler: function (request, reply) {
        reply("Hello Links!");
    }
})

On va donc choisir le verbe HTTP en le mettant comme valeur de la clé method qui accepte l'ensemble de verbes (à l'exception d'HEAD). Au cas où l'on veuille définir une route agnostique de la méthode on utilisera *, et si l'on veut juste un sous ensemble, on passera un tableau de méthodes.

Hapi accepte aussi des variables dans les url, avec une légère différence de syntaxe. Plutot que de les préfixer avec un :, celles-ci sont encapsulées dans des accolades: =path: '/links/{id}'=.

Dans les cas plus complexes on passera un objet de config contennant les différents paramètres. Ainsi on pourra configurer un grand nombre des aspect comme la validation des objets, d'authentification des utilisateurs, la mise en cache, et tout autre paramètre introduit par les différents plugin.

Notre route ressemblera ainsi à cela:

{
    method: 'POST', path: '/links',
    config: {
        handler: LinkController.create,
        validate: {payload: {url: Joi.string().required()}}
        /* autres paramètres de configurations  */
    }
}

Pour plus de détails on se réfèrera à la documentation.

4.2.3 Les handleurs de requêtes

Précédemment on est vite passé sur les handler de requêtes. Regardant plus en détail ce dont il s'agit.

Dans les deux cas il s'agit d'une fonction qui prend en argument deux objets représentant respectivement la réponse et la requête. C'est ici que l'on va implanter notre logique, le traitement que l'on veut associer aux requêtes.

Comme on peut s'y attendre, le premier objet va nous permettre d'accéder à tous les détails de la requête, ses headers, ses paramètres, et plus encore.

Il n'y a guère de différences à ce propos entre les deux framewoks. (si ce n'est la manière dont les objets sont peuplés, mais on y reviendra plus tard)

Le nom n'est pas si important, mais par convention en express on va plutot utiliser res et req, alors que request et reply sont utilisé pour hapi.

Cette différence fait sens pour la réponse vu que reply est une fonction dont l'appel va déclencher le début de création de la réponse.

En express l'envoi est déclenché par l'appel de la méthode end, send ou un des methodes helper similaires. (comme json())

Si dans hapi tout la requêtes et du début à la fin traité par le handler, en express via le système de middleware, une requête peut passer la main. Dans ce cas le handler aura trois argument, ce dernier étant une callback next() pour passer la main au handler suivant.

  1. Exemple concret

Pour illustrer cela, voila le même handler réalisé en Hapi et en Express.

Il s'agit de la méthode pour accéder à un lien particulier. En cas de succès on renvoi une représentation json du lien, en cas d'absence ou de suppression on renverra respectivement le code 404 et 410.

var expressHandler = function (req, res) {
    LinkDAO.get(req.params.id, function (link) {
        if (link == null) {
            res.status(404).end();
        } else {
            if (link.archived)
                res.status(410).end();
            else res.json(link)
        }
    });
};
var hapiHandler = function (request, reply) {
    LinkDAO.get(request.params.id, function (link) {
        if (link == null) {
            reply().code(404);
        } else {
            if (link.archived)
                reply().code(410);
            else reply(link);
        }
    });
};

On notera qu'en express la méthode va finaliser le traitement de la requête alors qu'il va l'initier en hapi

4.3 Interprétation de la requête

La requête contient nombre de données à différents endroits. Selon les cas on aura besoin d'aller chercher les informations dans le corps, les headers, les cookies, etc.

Si les deux frameworks offrent les mêmes fonctionnalités, l'approche de collecte est assez différentes.

4.3.1 Diversité des données à récupérer

Pour donner accès à ces information, les serveurs font offrir des objets pour les recupérer. Ceux-ci sont stockés dans diverses propriétés de l'objet représentant la réponse dans les handler.

Ainsi en Hapi on pourra accéder à la requête via query, entêtes via headers, paramètres de l'url via params, et information d'authentification via auth. il est aussi possible d'avoir accès au serveur via la propriété server. Il s'agit d'objets, donc on pourra accéder aux différents valeurs en utilisant au choix un accès statique (dot notation) ou dynamique (bracket notation). Par exemple, pour récupérer l'identifiant de la ressource demandé on utilisera request.params.id ou request.params['id']. Bien d'autres existent et on pourra consulter la liste [ici](http://hapijs.com/api#request- object).

Pour Express c'est assez similaire, avec les diverses propriétés de l'objet req dont path, params, query et bien d'autres.

L'ensemble des objets est consultable dans la documentation.

Quant au corps, avant de l'utiliser, surtout si on veut le parser en tant que document json, ceci doit être précisé au parseur.

4.3.2 Processus de collecte des corps

L'approche suivie pour peupler les representations des composants d'une requête est assez différentes entre Express et Hapi en accord avec leur philosophie. Si certaines sont fournies out of the box, pour d'autres il faut spécifier au server ce que l'on veut. Ceci est notamment le cas des cookies et du body.

En effet le parsage du corps n'est pas automatique. si on ne le précise pas on a notre beau json sous forme brute, textuelle.

Dans les deux cas on a besoin de dire au framework, que l'on souhaite parser les corps de messages.

Chez hapi on va juste configurer la route en lui précisant que l'on veut parser. Ceci ce fait via une des propriétés de l'objet de config de la route.

Pour express on intercallera en amont un middleware, bodyParser qui se chargera de lire le corps, de créer un objet javascript, et de l'insérer dans l'objet request.

Une fois chose faite, on pourra accéder au corps du message comme tout objet javascript, respectivement dans req.body pour express, et dans request.payload pour hapi.

Pour certains uses cases, potentiellement plus complexes, il existe des modules dédié à l'extraction de différentes données.

On a ainsi des middleware pour parser les cookies, des modules pour supporter les tableaux et objets dans les querystring (plugin qs pour hapi), ou d'autres pour gérer les sessions.

4.3.3 Quelques pointeur vers du concret

Dans pending link, les principales données nous intéressant sont le corps de requêtes dans le cas de la mise à jour, ou création de lien, les paramètres pour filtrer les collections, et les params pour cibler une sous ressource en particulier

Plusieurs fichiers permettent de notre prototype permettent d'illustrer plus en détails.

Si pour l'accès aux paramètres il faudra dans les deux framework les méthodes "controlleur", pour la configuration on regardera respectivement les routes pour hapi, et le chargement des middleware dans le serveur pour express.

§todo: add link to code

4.4 Création de la réponse

Une fois la requête parsée, le traitement associé effectuer, il faut gérer la réponse que l'on va renvoyer à notre cher client. Et pour cela, spécifier le code de retour, les headers, ainsi que le corps de la réponse HTTP

4.4.1 Codes de Retour HTTP

Tout d'abord, On ne le répétera jamais assez, il est crucial que les réponses renvoient un code de retour adéquat.

On fera donc l'effort de ne pas envoyer un 200 en cas d'erreur mais un 400 Bad request, 404 not found, un petit 500 ou plus approprié.

La spécification des codes de retour est assez similaire entre express et hapi, la principale différence étant le nom de la méthode. status pour exress, code pour Hapi.

//  express
 res.status(404).end()  // or shortcut sendStatus(404)
// hapi
reply().code(200);

Pour les deux, si non précisé par l'appel de ces méthodes la valeur par default est 200.

A noter qu'il existe en api des méthodes qui se chargeront à la fois de peupler la réponses et le code de retour, notation avec la méthode location qui met à la fois le code 201 Created et le header Location.

On pourra observer dans les contrôleurs respectif de notre prototypes les appels à ces méthodes ou nous utilisons selon les cas les codes 404, 410, 200, 201

§todo §LINK CODE

4.4.2 Utilisation des Headers HTTP

Parmi les autres incontournables des api Rest, l'utilisation des Header du protocole Http pour porter les metadonnnées de la requêtes et réponse, comme le type de contenu demandé ou envoyé, la fraicheur de la ressource disponible, et bien d'autres encore tant les utilisations sont innombrables.

Voici comment les headers sont spécifiés respectivement dans nos deux frameworks.

// express
res.set({'Content-type': 'text/plain', 'X-Custom': 'some-value'});
res.header('Lonely-Header', 'some-other-value');
res.type('json');

// hapi
reply().type('text/plain')
       .header('X-Custom', 'some-value')

En pratique la plupart des headers seront gérés semi-automatiquement enrichi respectivement par des middlewares ou plugins.

Dans notre prototype ou il n'y a pas de négociation de contenu, la spécification du header apparait qu'à un seul endroit, lors de la création d'une nouvelle ressources.

Dans les deux cas on a plusieurs méthodes pour ne pas à avoir à écrire à la main le nom de l'entête.

// express création
 res.location("/api/links/" + newLink._id);
// hapi creation
reply().created("/api/links/" + newLink._id);

4.4.3 Gestion du Corps du message

Si dans certains cas les métadonnées suffisent, dans d'autres il est bien nécessaire d'envoyer du contenu, une représentation de ressource.

Dans la plupart des cas, on se contentera d'envoyer un objet javascript correspondant à notre resource. Dans les deux framework, celui-ci sera automatiquement sérialisé en document json.

Pour ce faire, on passera notre réponse en argument dans la callback reply pour hapi, ou comme argument de la méthode json pour express

Il est tout aussi possible d'envoyer du texte brut en express en utilisant la méthode send.

Dans les deux framework il est possible de renvoyer des fichiers déjà constitués à la main. (méthode file)

Cependant on préférera utiliser les middlesware ou config correspondant pour servir des fichiers ou dossier entier s'il n'y a pas de traitement spécifique à la requête.

A noter que tout deux on aussi des fonctions pour servir des templates, mais ceci n'est pas l'objet de notre API. Si besoin est, on regardera les méthodes reply.view() et res.render()

4.5 TODO Express et Hapi au delà du basique

Partie bonux à venir:

  • Commment étendre notre serveur: middleware/plugin
  • Les must have

5 Conclusion: why we focus on Hapi

Après cette présentation globale d'hapi et express tant d'un point de vue principe que pratique j'espère avoir donné une bonne vue d'ensemble.

Si on devait résumer, tous les deux font le job, et ils le font bien. Au niveau du code lui même les similarités sont nombreuses, cependant comme on a pu le voir les différences sont grandes en termes d'architecture, mais aussi de portée et de philosophie.

En mettant en avant la configuration, en poussant à la conception d'applications modulaires aux composants indépendants, et en détectant les collisions de routes Hapi se détache d'express dans un contexte industriel.

S'il s'agit d'exposer une api sur un site existant basé sur express, ou si on réalise une api de taille modeste ou un prototype, en solo, avec une expérience préalable d'express, dans ce cas il n'est ni nécessaire ni avisé de changer pour hapi.

Par contre si on s'apprête à faire une api d'envergure, qui est la pour durer, ou une collections de micro service, au sein d'une équipe dans ce cas Hapi est clairement plus adapté. Surtout que celui-ci permet de facilement mettre en place une stratégie de proxy pour exposer une API s'appuyant sur un backend existant, et d'ainsi migrer vers une architecture REST tout en douceur.

et puis avant d'oublier l'argument final,

because I'm hapi…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment