S’il y a bien une chose qui craint avec les technologies « cool » et récentes, c’est la doc. Et Node.JS (ou plutôt la grande majorité de ses modules, car l’API de Node elle-même est plutôt bien documentée) n’échappe pas à la règle, et les contributions de @visionmedia quoi que d’excellentes facture et très populaires, en sont hélas une illustration
Du coup, il y a des fonctionnalités qu’on ne découvre qu’en farfouillant dans le code…
Je refléchissais en ce moment à un moyen de « packager » les applications Express dans Node.JS, de manière à les réutiliser. Naïvement, j’étais persuadé que ça n’était pas possible, et je suis parti dans le développement d’un framework permettant de simplifier ce packaging, avec des vues embarquées, etc… Naïf, oui, car évidemment cette fonctionnalité existe déjà dans Connect (la couche sous Express): quand on appelle app.use(une_autre_app), c’est comme si on « fusionnait » les deux applications. Ce comportement, ainsi que les valeurs de configuration disponibles, étant très mal documentées, je vais vous en parler ici
L’application à partager
Supposons que j’ai une application « forum », avec la structure habituelle:
+- app.js +- package.json +- views/ +- layout.ejs +- index.ejs +- thread.ejs |
En substance, l’application est de cette forme:
var app = require('express').createServer().configure(function() { // ... Configuration this.get('/', /* liste des threads */) this.get('/threads', /* idem */) this.get('/thread/:id', /* affichage d'un thread */) }) app.listen(8080) |
Le code est disponible en intégralité sur gist.github.com (pour alléger un peu l’article). Une démonstration (gracieusement hébergée par DotCloud, dont il faudra que je vous parle un jour
) est également disponible en ligne.
Un simple npm install pour télécharger les dépendances, un petit node app et c’est parti, localhost:8080 vous affichera la même chose que demoexpress1.naholyr.dotcloud.com.
Préparer l’application pour être réutilisable
Dans un premier temps, il faut que je transforme mon application en module pouvant être importé dans un autre script. En l’état, si j’appelle require('./app'), le serveur démarre directement. Il faut donc que l’appel à listen() dépende de la façon dont on fait appel à l’application.
Je vais donc dans un premier temps rendre mon application « exportable » (un require('./app') retournera l’application elle-même):
/** Déclaration de l'application */ -var app = require('express').createServer().configure(function() { +var app = module.exports = require('express').createServer().configure(function() { |
En plus de ça, il ne faut que l’application ne démarre directement que dans le cas où le module est exécuté directement par node. L’objet require permet de détecter ce type de différence:
/** On démarre le serveur sur le port 8080 */ -app.listen(8080) +if (require.main === module) { + app.listen(8080) +} |
Bien sûr une autre méthode aurait été d’avoir deux script: un app.js qui ne fait que déclarer l’application, et un server.js qui l’inclut et appelle sa méthode listen(). Bref, ça marche.
Réutiliser l’application
Maintenant, supposons qu’on a une toute autre application, qui d’ailleurs utilise jade, et dans laquelle on souhaiterait réutiliser notre forum, avec des URLs préfixées par « /forum/… ».
Dans un premier temps on va prendre notre première application et la poser dans un dossier lib/forum par exemple, puis simplement dire à notre application de l’embarquer à l’aide de use():
var express = require('express') var app = express.createServer() // ... ici toute la configuration de mon application ... // var forumApp = require('./lib/forum') app.use('/forum', forumApp) app.listen(8080) |
Je démarre mon application, je vais sur http://localhost:8080 (OK, ça affiche mon index) puis http://localhost:8080/forum et là paf! Je vois mon forum
Bon, par contre:
- Je n’ai pas le layout de mon application actuelle, mais seulement celui du forum
- Je n’ai pas non plus le titre de mon application principale…
- Mes liens ne marchent pas, en effet
/threadsn’est pas reconnu, c’est normal il faudrait plutôt que ce soit/foru/ threadsvu qu’on a « monté » l’application sur/forum
Pour tout ça, il suffit de jouer sur le « view options« . Dans un premier temps on va déjà corriger les liens, en rendant configurable via une variable de template dans mon application « forum » la racine des URLs:
@@ lib/forum/app.js this.set('view options', { - title: 'Forum' + title: 'Forum', + rootUrl: '/' @@ lib/forum/views/index.js - <li><a href="/thread/<%= threads[i].id %>">Thread #< %= threads[i].id %> + <li><a href="<%= rootUrl %>thread/< %= threads[i].id %>">Thread #< %= threads[i].id %> @@ lib/forum/views/thread.js - <p><a href="/threads">Back to index</a></p> +<p><a href="<%= rootUrl %>threads">Back to index</a></p> </a></li></a></li> |
Je n’ai plus qu’à, dans mon application principale, reconfigurer l’application intégrée pour qu’elle corresponde mieux à mes besoins:
// ... var forumApp = require('./lib/forum') forumApp.set('view options', { title: app.set('view options').title, layout: app.set('views') + '/layout.' + app.set('view engine'), rootUrl: '/forum' }) // ... |
On a ainsi:
- spécifié qu’on utilisait le même titre global que l’application en cours (parce que par convention, tant qu’à faire, on avait aussi défini une variable «
title« ) - indiqué le chemin complet du layout de notre application, afin que l’app embarquée n’essaie pas d’utiliser le sien à chaque appel
- spécifié la «
rootUrl» afin de modifier les liens
Retour sur http://localhost:8080/forum/threads bingo, on a bien notre style, et nos liens fonctionnent
Là encore, le code est à votre disposition sur gist.github.com et une démo est présente sur demoexpress2.naholyr.dotcloud.com.
Conclusion
Même si ça manque de documentation, et de standards, tout est là pour que ça fonctionne
Pour que votre application puisse être réutilisée dans d’autres applications Express, il faut bien la développer de manière à ce que les URLs soient configurables (au moins la racine). Je conseillerais de passer par un helper dédié à la génération de vos URLs, ça simplifierait fortement ce travail. Je n’ai sans doute pas encore exploré tous les problèmes qui peuvent survenir, comme la réutilisation des partials… Quand j’aurai fait le tour de la question, je publierai un nouvel article avec cette fois-ci des conventions précises, ainsi que quelques modules permettant de simplifier ce travail (notamment la génération des URLs).

Comments:5
Laisser un commentaire