1

Mon but est de pouvoir écrire des routes pures pour un serveur Express.js. Est-ce que c'est possible?Serveur Express.js avec programmation fonctionnelle (routes pures)

Pour accéder à la base de données et à d'autres choses, je sais que je peux utiliser la fabuleuse monade Future pour garder les choses pures, mais qu'en est-il du rendu de la route?

Une des plus grandes difficultés que je me rends compte est qu'une route pourrait se terminer par un bon nombre de différentes manières, comme:

  • redirection
  • modèle
  • rendu
  • retour d'erreur
  • JSON retour
  • retour de fichier

Avec la monade Future, je peux gérer l'erreur et le cas de succès, mais il n'y a pas beaucoup plus de granularité à la réussite après cela.

Existe-t-il un moyen d'écrire des routes pures et totalement testables pour Express.js?

+1

Vous devriez fournir des exemples de code d'itinéraires avec lesquels vous essayez de travailler - peut-être montrer des choses que vous avez essayées aussi? – naomik

+0

Quelque chose de simple comme une route pour montrer tous les articles dans un panier, par exemple. Je voudrais voir comment cela pourrait être implémenté de manière pure étant donné que l'appel de 'res.render' ou' res.redirect' ou même l'assignation de valeurs à 'res.local' sont des choses impures. Comment pourrais-je créer une fonction pure pour gérer ces cas? –

+0

@MarceloLazaroni, [ici] (https://gist.github.com/beardedtim/d54bf38a02ceb4df81f71c5ff52deddf) est un aperçu de ce que j'essaie afin de rendre mes itinéraires plus fonctionnels. –

Répondre

2

Réponse courte: Non - Ce n'est pas possible.

Explication: Dans le contexte de la programmation fonctionnelle, nous avons flux de données - programme prend des données d'entrée, qui transforment et renvoie les données de sortie.

Dans le cas du serveur, nous avons deux flux de données. La première est lorsque vous démarrez un serveur. Dans ce flux, vous pourriez vouloir lire le fichier de configuration ou les paramètres de ligne de commande du monde externe pour des choses comme le port, l'hôte, la chaîne de base de données, etc. Ceci est un effet secondaire. Exemple,

readJson(process.argv[2]) // Read configuration file 
    .chain(app)    // Get app instance (routes, middlewares, etc.) 
    .chain(start)    // Start server 
    .run().promise() 
     .then((server) => info(`Server running at: ${server.info.uri}`)) 
     .catch(error); 

Ceci est un fichier index.js typique contenant tous les effets secondaires (lecture config). Passons maintenant au deuxième flux de données.

Ce flux de données est légèrement difficile à imaginer. La sortie/l'effet secondaire du premier flux de données est l'écoute du serveur sur un port pour une connexion externe. Maintenant, imaginons que chaque requête arrive sur ce serveur en tant que flux de données indépendant en lui-même.

Tout comme index.js était le fichier destiné à gérer tous les effets secondaires, votre itinéraire fichier ou la fonction itinéraire gestionnaire est destiné à traiter la demande effet secondaire à savoir résultat devrait être répondu en arrière à ce gestionnaire d'itinéraire . Généralement, route fonctionnelle proche de pure ressemblerait à:

function handler(request, reply) { 

    compose(serveFile(reply), fileToServe)(request) 
     .orElse((err) => err.code === 'ENOENT' ? reply404(reply) : reply500(reply)) 
     .run(); // .run() is the side effect 
} 

return { 
    method: 'GET', 
    path: '/employer/{files*}', 
    handler 
}; 

Dans l'extrait ci-dessus, tout est pur. Seul le truc impur est .run() méthode (J'utilise Hapi.js et Folktale.js Task).

Identique à l'idée avec les frameworks Front-End comme Angular ou React. Les composants dans ce cadre doivent contenir tous les effets/impuretés. Ces composants, tout comme vos gestionnaires d'itinéraire sont des points d'extrémité où les effets secondaires doivent se produire. Vos modèles/services devraient être exempts d'impureté.Cela dit, si vous voulez toujours rendre vos routes parfaitement pures, il y a de l'espoir. Ce que vous voulez faire est essentiellement - plus haut niveau d'abstraction:

  1. Si express.js est un cadre, vous devez construire vos propres abstractions au-dessus de celui-ci.
  2. Vous écrivez généralement votre propre gestionnaire d'itinéraire générique pour toutes vos routes et vous exposez votre propre API pour enregistrer des routes.
  3. Vos gestionnaires d'itinéraire renverront alors le futur/tâche/observable ou toute autre monade qui contient tous les effets secondaires attendant d'être libérés.
  4. Votre gestionnaire de routes abstraites appellera simplement .fork() ou .run(). Cela signifie que lorsque vous testez vos gestionnaires d'itinéraires, aucun effet secondaire ne se produit. Ils sont simplement enveloppés dans une Monade Async.

Dans le cas de cadres frontaux, comme je l'ai dit précédemment, vos composants de l'interface utilisateur auront des effets secondaires. Mais il y a de nouveaux cadres qui vont au-delà de cette abstraction. Cycle.js est un cadre que je connais. Il applique un niveau d'abstraction plus élevé que Angular ou React. Tous les effets secondaires sont mis en cage dans des observables qui sont ensuite envoyés au framework et exécutés en faisant tout (je veux dire littéralement 100%) vos composants purs.

J'ai essayé de créer un serveur comme vous le faites en utilisant Hapi.js + Folktale.js + Ramda. Mon idée à l'origine retrace ses racines dans Cycle.js. Cependant, j'ai obtenu un léger succès. Un code de pièce était vraiment gênant et trop restrictif pour écrire. Après, j'ai renoncé à avoir des routes pures. Cependant, le reste de mon code est pur et est assez lisible. En guise de conclusion, la programmation fonctionnelle concerne le codage par parties et non le codage en entier. Ce que vous ou moi essayons de faire, c'est une programmation fonctionnelle complète. Cela va sembler peu gênant au moins en JavaScript.