J'ai démarré il y a peu un nouveau projet avec un backend en nodejs. J'en ai profité pour me remettre à jour et appliquer ce que j'ai appris coté front ces derniers mois : les décorateurs. J'ai préparé un starter project dont la particularité est d'être écrit en ES6+ transpilé avec Babel et de faire usage de décorateurs pour décrire les routes.
Pour créer un nouvel ensemble de routes, il suffit de rajouter un fichier dans le dossier server/routes/mon-controller.js
et d'exporter une classe décorée avec @routes
. On peut alors décorer chacune de ses méthodes avec un nom de méthode http. Ainsi :
import {routes, get} from '../decorators/routes';
import {bikesFetcher} from '../fetchers/bikes';
@routes
export class Bikes {
@get({ route: '/bikes' })
getBikes (req, res) {
bikesFetcher.getAll()
.then(bikes => res.json(bikes);
.catch(err => res.status(500).send(err));
}
@get({ route: '/bikes/:id' })
getBikeById(req, res) {
bikesFetcher.getById(req.params.id)
.then(bike => res.json(bike);
.catch(err => res.status(500).send(err));
}
}
Si on fouille dans la définition de @routes
on découvre que l'app express doit être passée dans le constructor et est utilisée pour définir les routes en lisant une statique _routes
, elle même renseignée par chacun des décorateurs de méthode, @get
par exemple. C'est lors de l'instanciation automatique de chacun des modules de routes que ceux-ci sont construits avec l'app en paramètre.
On peut passer un argument middlewares
à @routes
et @[method]
. Il s'agit d'un ou d'un tableau de middlewares qui seront donc exécutés avant la route concernée. On peut le passer sur @routes
afin qu'il s'applique à toutes les routes de la classe, ou sur une route en particulier. Par exemple avec un middleware d'authentification :
export function auth (req, res, next) {
if (req.user) {
return next();
}
res.status(401).send('Auth failed');
}
import {routes, get} from '../decorators/routes';
import {bikesFetcher} from '../fetchers/bikes';
import {auth} from '../auth';
@routes
export class Bikes {
// […]
@post({ route: '/bikes/:id', middlewares: auth })
createBike(req, res) {
bikesFetcher.create(req.body)
.then(bike => res.json(bike);
.catch(err => res.status(500).send(err));
}
}
Ici, les deux méthodes de lectures sont accessibles à tous, mais la méthode de création de vélo n'est accessible qu'avec une session valide. On pourrait rendre l'accès aux vélos entièrement inaccessible sans session en passant le middleware à la classe :
import {routes, get} from '../decorators/routes';
import {bikesFetcher} from '../fetchers/bikes';
import {auth} from '../auth';
@routes({ middlewares: auth })
export class Bikes {
// […]
@post({ route: '/bikes/:id' })
createBike(req, res) {
bikesFetcher.create(req.body)
.then(bike => res.json(bike);
.catch(err => res.status(500).send(err));
}
}
Tout cela est dispo librement sur github, je vous invite à y jeter un œil et à me proposer éventuellement des pull requests ;)